Introduction

Traumatic brain injury (TBI) frequently leads to lasting neurological deficits, driven largely by a strong yet unresolved noninflammatory reaction. To better understand this process, I re-analyzed the published snRNA-seq dataset GSE209552 Garze et al, 2023, to take advantage of their single cell resolution.

Figure 1: Data overview (Garza et al, 2023; Supplemental table 1)
Figure 1: Data overview (Garza et al, 2023; Supplemental table 1)

Data Download

# Define global constant
GSE_NUM <- "GSE209552"

GEOmetadb setup

To setup I followed GEOmetadb Tutorial Chapter 3.

# Install required package if not installed
if (!requireNamespace("BiocManager", quietly = TRUE))
  install.packages("BiocManager")
if (!requireNamespace("GEOmetadb", quietly = TRUE))
  BiocManager::install("GEOmetadb")
if (!requireNamespace("DBI", quietly = TRUE))
  install.packages("DBI")
if (!requireNamespace("RSQLite", quietly = TRUE))
  install.packages("RSQLite")
if (!requireNamespace("dplyr", quietly = TRUE))
  install.packages("dplyr")
if (!requireNamespace("ggplot2", quietly = TRUE))
  install.packages("ggplot2")

# Attach packages
library(GEOmetadb)
library(DBI)
library(RSQLite)

# Download SQLite database if not exist
if (!file.exists('GEOmetadb.sqlite')) getSQLiteFile()
file.info('GEOmetadb.sqlite')

# Connect to database
con <- dbConnect(SQLite(), 'GEOmetadb.sqlite')

Download whole GSE209552

I followed Identifier Mapping Tutorial. The helper function defined below is modified based on Identifier Mapping Tutorial’s helper function to avoid duplicated download.

# Define helper function
fetch_geo_supp <- function(gse, destdir = "data") {
  # Calculate the actual path where GEOquery puts the files
  target_path <- file.path(destdir, gse)
  
  # Check if directory exists AND contains files
  if (dir.exists(target_path) && length(list.files(target_path)) > 0) {
    message(paste("Data found at", target_path, "- Skipping download."))
    return(invisible(target_path))
  }
  
  # If missing or empty, proceed with download
  message(paste("Downloading", gse, "to", destdir, "..."))
  dir.create(destdir, showWarnings = FALSE, recursive = TRUE)
  GEOquery::getGEOSuppFiles(GEO = gse, baseDir = destdir, makeDirectory = TRUE)
  
  invisible(target_path)
}

# Download
fetch_geo_supp(gse = GSE_NUM)

Construct snRNA-seq only reference list

This GSE contain both snRNA-seq and bulk RNA-seq sample. Removal of bulk RNA-seq is needed to avoid significant batch effect due to their technical difference. Although the number of samples is reduced to 17, it’s still sufficient amount.

Refer to the original paper’s method section (Garza et al, 2023), only the snRNA-seq was done by Illumina NextSeq6000 platform, GPL24676. I can use this clue to exclude all bulk RNA-seq.

Since all GSMs of this study are packed in one GSE209552_RAW.tar file in GEO, I can’t indicate which platform to include/exclude at download step. Thus I constructed a list of GPL24676 GSMs for downstream reference by query GEOmetabd.

# Define desired platform
snRNA_platform <- "GPL24676"

# Construct the query
# We join the 'link' table (gse_gsm) with the 'sample' table (gsm) 
# to check the platform (gpl) column.
sql_filter <- paste0(
  "SELECT t1.gsm ",
  "FROM gse_gsm t1 ",
  "JOIN gsm t2 ON t1.gsm = t2.gsm ",
  "WHERE t1.gse = '", GSE_NUM, "' ",
  "AND t2.gpl = '", snRNA_platform, "'"
)

# Get the list of pure snRNA-seq samples
sn_gsm_list <- dbGetQuery(con, sql_filter)$gsm

# View results
print(paste("Found", length(sn_gsm_list), "snRNA-seq samples."))
[1] "Found 17 snRNA-seq samples."
sn_gsm_list
 [1] "GSM6376803" "GSM6376804" "GSM6376805" "GSM6376806" "GSM6376807"
 [6] "GSM6376811" "GSM6376812" "GSM6376813" "GSM6376814" "GSM6376815"
[11] "GSM6376816" "GSM6376817" "GSM6376818" "GSM6376819" "GSM6376820"
[16] "GSM6376821" "GSM6376822"

Access data quality

# Install Seurat if not
if (!requireNamespace("Seurat", quietly = TRUE))
  install.packages("Seurat")

Reorganize snRNA-seq files

The raw count data was stored in GSE209552_RAW.tar file, thus need to be unpacked using untar() function.

# Attach packages
library(Seurat)
library(data.table)
library(Matrix)
library(dplyr)
library(stringr)

# Define data paths
tar_file <- "data/GSE209552/GSE209552_RAW.tar"
extract_dir <- "data/GSE209552/raw_files"

# Define unpack helper function
unpack_geo_tar <- function(tar_path, dest_dir) {
  if (dir.exists(dest_dir) && length(list.files(dest_dir)) > 0) {
    message(paste("📂 Extracted files found at", dest_dir, "- Skipping unpack."))
    return(invisible(dest_dir))
  }
  if (!file.exists(tar_path)) {
    stop(paste("❌ Tar file not found:", tar_path))
  }
  message(paste("📦 Unpacking", basename(tar_path), "..."))
  dir.create(dest_dir, showWarnings = FALSE, recursive = TRUE)
  untar(tar_path, exdir = dest_dir)
  message("✅ Unpacking complete.")
  return(invisible(dest_dir))
}

# Run the unpacker
unpack_geo_tar(tar_file, extract_dir)

The unpacked file is very messy, contain all 10x genomics snRNA-seq files, bulk RNA-seq files, subsetted Transposable Elements file, clustered files, and normalized files. Among those, only 10x genomics snRNA-seq is what I need.

To extract, I utilize previously defined sn_gsm_list, loop over it, exclude any file contain _TE_, indicate Transposable Elements, only extract files end with either barcode, matrix, or features. Lastly, trim the file name to barcode, matrix, features only, to meet the expected naming format of Seurat’s Read10X() function.

# Reorganize all snRNA-seq 10x files in to a new directory
dest_dir <- "data/GSE209552/10x_organized"    # Where they should go
dir.create(dest_dir, recursive = TRUE, showWarnings = FALSE)

# --- MAIN LOOP ---
message(paste("🚀 Organizing files for", length(sn_gsm_list), "samples..."))

skipped_count <- 0
moved_count   <- 0

for (gsm in sn_gsm_list) {
  
  # 1. Define the specific destination path for this sample
  sample_subdir <- file.path(dest_dir, gsm)
  
  # 2. CHECK: Does it already exist?
  if (dir.exists(sample_subdir) && length(list.files(sample_subdir)) >= 3) {
    skipped_count <- skipped_count + 1
    next
  }
  
  # 3. Create directory
  dir.create(sample_subdir, showWarnings = FALSE)
  
  # 4. Find source files (Exclude the 'TE' files)
  pattern <- paste0("^", gsm)
  files_to_move <- list.files(extract_dir, pattern = pattern, full.names = TRUE)
  files_to_move <- files_to_move[!grepl("_TE_", files_to_move)]
  
  # Safety Checks
  if (length(files_to_move) == 0) {
    warning(paste("⚠️ Skipping", gsm, "- No source files found."))
    next
  }
  
  # 5. MOVE & RENAME
  # We identify specific files to ensure we rename them correctly for Seurat
  f_matrix   <- files_to_move[grep("matrix", files_to_move, ignore.case = TRUE)]
  f_barcodes <- files_to_move[grep("barcodes", files_to_move, ignore.case = TRUE)]
  f_features <- files_to_move[grep("features|genes", files_to_move, ignore.case = TRUE)]
  
  # Verify we found exactly 1 of each before moving
  if (length(f_matrix) == 1 && length(f_barcodes) == 1 && length(f_features) == 1) {
    
    # Rename Matrix -> matrix.mtx.gz
    file.rename(f_matrix, file.path(sample_subdir, "matrix.mtx.gz"))
    
    # Rename Barcodes -> barcodes.tsv.gz
    file.rename(f_barcodes, file.path(sample_subdir, "barcodes.tsv.gz"))
    
    # Rename Genes/Features -> features.tsv.gz
    file.rename(f_features, file.path(sample_subdir, "features.tsv.gz"))
    
    message(paste("  ✅ Standardized & Moved:", gsm))
    moved_count <- moved_count + 1
    
  } else {
    warning(paste("  ⚠️ Skipping", gsm, "- Could not find unique 10x triplet to standardize."))
  }
}

# --- SUMMARY ---
message("🎉 Organization Complete.")
message(paste("  🔹 Samples moved & standardized:", moved_count))
message(paste("  🔸 Samples skipped (already done):", skipped_count))

Load snRNA-seq files into R using Seurat

Loading of expression data is done by iteratively call Seurat’s Read10X() and CreateSeuratObject() function over all GSMs. They cooperatively first read a 10X genomics structured file and create a Seurat Object, which is the basic unit for any data manipulation in Seurat.

# Get the list of sample subdirectories
sample_dirs <- list.dirs(dest_dir, recursive = FALSE)

# Define a list storing seurat obj of all GSMs
seurat_list <- list()

message(paste("🚀 Loading", length(sample_dirs), "samples..."))
for (dir in sample_dirs) {
  # Extract GSM ID from the folder name (e.g., "GSM6385438")
  gsm_id <- basename(dir)
  
  # A. Read 10x Data
  # Because you renamed the files, Read10X finds them automatically!
  counts <- Read10X(data.dir = dir)
  
  # B. Create Seurat Object
  seurat_list[[gsm_id]] <- CreateSeuratObject(
    counts = counts, 
    project = gsm_id, 
    min.cells = 0, 
    min.features = 0
  )
  
  message(paste("  ✅ Loaded:", gsm_id))
}
length(seurat_list)
[1] 17

However, the metadata (contain condition information) need to fetched from GEOmetadb, and extract from GSM table’s characteristics_ch1 column, and then insert into corresponding Seurat object’s meta.data layer.

# Load GSMs metadata from GEOmetadb
gsm_list_string <- paste(paste0("'", sn_gsm_list, "'"), collapse = ",")
sql <- paste0(
  "SELECT gsm, title, characteristics_ch1 ",
  "FROM gsm ",
  "WHERE gsm IN (", gsm_list_string, ")"
)
metadata <- dbGetQuery(con, sql)

# Merge the condition metadata to corresponding Seurat obj's metadata
metadata$Condition <- sub(".*condition:\\s*([^;]+).*", "\\1", metadata$characteristics_ch1, ignore.case = TRUE)

# Loop and assign
for (gsm in names(seurat_list)) {
  # Find the condition for this GSM
  val <- metadata$Condition[metadata$gsm == gsm]
  
  # Assign to Seurat object (handling missing values)
  if (length(val) > 0) {
    seurat_list[[gsm]]$Condition <- val
  } else {
    seurat_list[[gsm]]$Condition <- "Unknown"
  }
}

# Final Merge all samples into one seurat obj
combined_seurat <- merge(
 x = seurat_list[[1]], 
 y = seurat_list[-1], 
 add.cell.ids = names(seurat_list), 
 project = "GSE209552"
)
# Verify
table(combined_seurat$Condition)

control     tbi 
  10666   13955 

Free memory

# Disconnect SQLite connection
dbDisconnect(con)

# Delete memory pointer from R
rm(con)
rm(seurat_list)
rm(counts)
rm(metadata)

# # De allocate unused memory
gc()
            used   (Mb) gc trigger   (Mb)   max used   (Mb)
Ncells   9930919  530.4   18916009 1010.3   18916009 1010.3
Vcells 288165847 2198.6  898835015 6857.6 1102761272 8413.5

Data exploriation

Plot the following statistics:

  • Number of unique identifier (infer coverage)
  • Number of total amount of identifier (infer depth)
  • Number of samples
  • Number of cells
library(ggplot2)
library(patchwork)

theme_set(theme_classic(base_size = 26))

# 1. Coverage (nFeature_RNA)
p_cov <- VlnPlot(
  combined_seurat, 
  features = "nFeature_RNA", 
  group.by = "Condition", 
  pt.size = 0
) + 
  ggtitle("Coverage") + 
  ylab("nFeature_RNA")

# 2. Depth (nCount_RNA) with adjusted axis
p_depth <- VlnPlot(
  combined_seurat, 
  features = "nCount_RNA", 
  group.by = "Condition", 
  pt.size = 0
) + 
  ggtitle("Depth") + 
  ylab("nCount_RNA") +
  scale_y_continuous(limits = c(0, 50000)) 

# Combine them to recreate p_metrics
p_metrics <- p_cov | p_depth

# Number of cells among conditions
meta_data <- combined_seurat@meta.data

# A. Bar plot: Total Number of Cells per Condition
p_cells <- ggplot(meta_data, aes(x = Condition, fill = Condition)) +
  geom_bar(color = "black") +
  geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Cells per Group", y = "Number of Cells", x = NULL) +
  theme(legend.position = "none")

# Number of samples among conditions
p_samples <- meta_data %>%
  group_by(Condition) %>%
  summarize(Sample_Count = n_distinct(orig.ident)) %>%
  ggplot(aes(x = Condition, y = Sample_Count, fill = Condition)) +
  geom_bar(stat = "identity", color = "black") +
  geom_text(aes(label = Sample_Count), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Samples (GSMs) per Group", y = "Number of Samples (GSMs)", x = NULL) +
  theme(legend.position = "none")

# Merge plots
final_plot <- p_metrics / (p_samples | p_cells)

# Adjust font size
final_plot <- final_plot & theme_classic(base_size = 16)
final_plot

Figure 2: Overall statistics of dataset GSE209552 grouped by condition (Control v.s. tbi). The upper-left panel indicate the number of unique identifier; The upper-right panel indicate the total amount of identifier, the maximum amount of identifier was limited up to 50000 for the sake of aesthetics; The lower-left panel indicate number of samples (GSMs); The lower-right panel indicate the number of cells”; X-axis of all four panel indicate condition.

Generally, control group have higher depth and coverage, but less sample and cells.

Then plot the statistics below per sample (GSM):

  • Number of unique identifier (infer coverage)
  • Number of total amount of identifier (infer depth)
  • Number of cells

# Define the vertical line style once to keep it consistent
vertical_line <- geom_vline(xintercept = 5.5, linetype = "dashed", color = "red", size = 1)

# 1. Coverage (nFeature_RNA)
p_cov <- VlnPlot(
  combined_seurat, 
  features = "nFeature_RNA", 
  group.by = "orig.ident", 
  pt.size = 0
) + 
  ggtitle("Coverage") + 
  ylab("nFeature_RNA") +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none",
    axis.title.x = element_blank() # Often cleaner to remove x-label if text is rotated
  ) + vertical_line

# 2. Depth (nCount_RNA) with adjusted axis
p_depth <- VlnPlot(
  combined_seurat, 
  features = "nCount_RNA", 
  group.by = "orig.ident", 
  pt.size = 0
) + 
  ggtitle("Depth") + 
  ylab("nCount_RNA") +
  scale_y_continuous(limits = c(0, 50000)) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none",
    axis.title.x = element_blank()
  ) + vertical_line

# A. Bar plot: Total Number of Cells per Sample
meta_data <- combined_seurat@meta.data # Ensure meta_data is defined
p_cells <- ggplot(meta_data, aes(x = orig.ident, fill = orig.ident)) +
  geom_bar(color = "black") +
  geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Cells per Sample", y = "Number of Cells", x = NULL) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none"
  ) +
  coord_fixed(ratio = 0.0005)  +
  scale_y_continuous(limits = c(0, 6000)) + vertical_line

# Merge plots
final_plot <- p_cov / p_depth / p_cells + 
  plot_layout(heights = c(1, 1, 1))

# Adjust font size (and re-apply theme elements if needed by the base theme)
final_plot <- final_plot & theme_classic(base_size = 16) & 
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none"
  )

final_plot

Figure 3: Overall statistics per sample (GSM). The top panel indicate number of unique identifier; The middle panel indicate number of total identifier; The bottom panel indicate number of cells; X-axis of all three panel indicate samples (GSMs); The vertical red dash line separate control (left side of red line), and tbi (right side of red line).

In general, the sequence depth and coverage is more consistence among control group. Tbi group has more variated depth and coverage with GSM637681 being the sample of lowest depths and coverage. The number of cells within different sample also varies, from highest 3708 (GSM6376822) to lowest 358 (GSM6376820). This imbalance in cell number may result in downstream analysis dominate by sample which contain more cells.

Another common quality control step in scRNA-seq is to remove damaged cells. When damaged cells were sequenced or damaged during sequencing, the expression intensity will scaled down due to leaked RNA, thus significantly bias downstream clustering, annotation, and differential gene expression analysis. A common method to conduct this filtering is using mitochondrial transcript percentage, based on this idea that if a cell has leaked RNA, the mitochondrial transcript percentage will be relatively higher since mitochondria is relatively large and less likely to leak. Empirically, the threshold is set to be 5% ~ 10%

However, when it comes to snRNA-seq, mitochondrial transcript percentage is expected to be 0% because of the exclusion of cytoplasm, and any none-zero mitochondrial transcript percentage indicate incomplete exclusion of cytoplasm Amezquita et al, 2020. Thus I may want to choice a more strict threshold for snRNA-seq, like 1% ~ 2%, to match the wet-lab design while tolerate low-quality cell to certain extent.

# Use seurat PercentageFeatureSet() function
# Create a new entry in meta.data layer storing mitochondrial transcript percentage
combined_seurat[["percent.mt"]] <- PercentageFeatureSet(combined_seurat, pattern = "^MT-")

# Defin a horizontal line at Y == 5 indicate empirical mito% threashold
horizontal_line <- geom_hline(yintercept = 10, linetype = "dashed", color = "red", size = 1)

# Percent MT per CONDITION (Control vs TBI)
p_condition <- VlnPlot(combined_seurat, features = c("percent.mt"), group.by = "Condition", pt.size = 0) +
  theme(
    axis.title.x = element_blank(), 
    legend.position = "none" # Hide legend since x-axis already shows the names
  ) + 
  ggtitle("Mitochondrial % by Condition") + 
  horizontal_line + 
  ylab("Mitochondrial Transcript (%)") + 
  annotate("text", x = 0.5, y = 12, label = "mito % = 10", color = "black", hjust = 0, size = 5)

# Percent MT per GSM
p_sample <- VlnPlot(combined_seurat, features = c("percent.mt"), group.by = "orig.ident", pt.size = 0) +
  theme(
    axis.title.x = element_blank(), 
    # Rotate x-axis labels 45 degrees so they don't overlap
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none" # Hide legend since x-axis already shows the names
  ) + vertical_line + 
  ggtitle("Mitochondrial % by Sample") + 
  horizontal_line + 
  ylab("Mitochondrial Transcript (%)") + 
  annotate("text", x = 0.5, y = 12, label = "mito % = 10", color = "black", hjust = 0, size = 5)

# 4. Combine them
final_mt_plot <- p_condition / p_sample
final_mt_plot

Figure 4: Mitochondrial transcript percentage grouped by GSMs. The Y-axis indicate mitochondrial transcript percentage; The X-axis of top panel indicate condition (Control v.s.); The X axis of bottom panel indicate GSM samples; The horizontal red dash line indicate empirical Mitochondrial transcript percentage threshold = 10%; The vertical red dash line at bottom panel separate control (left side of red line), and treatment (right side of red line).

However, the Mitochondrial transcript percentage is way higher than expected in all samples and the original paper didn’t assess the Mitochondrial transcript percentage at all (Garza et al, 2023). One clue that may explain this unexpected high Mitochondrial transcript percentage from their Limitations of the study is control tissues were post-mortem sample and tbi tissues were surgically evacuated rare case post-impact. Thus the integrity of samples were not at ideal condition and expected to contain damaged cells.

On the other hands, mitochondrial transcript percentage aligned with previous depth and coverage among samples, where control group tend to be more consistent. A hypothesis is that Traumatic Brain Injury (TBI) is a broad term that include various brain damage result from external force, and the intensity and exact pathology may vary among different tbi sample, reflected by sequence depth and coverage. Noticeably, GSM6376814 has relatively higher mitochondrial transcript percentage, and recall that it’s also the sample having lowest depth and coverage, which might imply existing technical variance and/or batch effect that could bias downstream analysis.

Mapping to HUGO symbols

Check the current identifier

head(rownames(combined_seurat))
[1] "MIR1302-2HG" "FAM138A"     "OR4F5"       "AL627309.1" 
[5] "AL627309.3"  "AL627309.2" 
length(rownames(combined_seurat)) == length(unique(rownames(combined_seurat)))
[1] TRUE

At a glance, it’s HUGO symbol. To verify, I can query those symbol against org.Hs.eg.db. And TRUE indicate the number of gene symbol is equal to the number of unique gene symbol, thus no duplicate.

# Install org.Hs.eg.db
if (!requireNamespace("org.Hs.eg.db", quietly = TRUE)) {
  BiocManager::install("org.Hs.eg.db")
}

# Attach org.Hs.eg.db to current session
library(org.Hs.eg.db)

# Check how many of them exist in the official symbol database
valid_symbols <- mapIds(org.Hs.eg.db, 
                        keys = rownames(combined_seurat), 
                        column = "SYMBOL", 
                        keytype = "SYMBOL", 
                        multiVals = "first")

# Calculate the percentage of matches
sum(!is.na(valid_symbols)) / length(rownames(combined_seurat)) * 100
[1] 100

100% valid symbols confirmed the symbol is actually HUGO symbol.

Cleaning

First I remove GSM6376814, since it has the lowest depth, coverage, and highest mitochondrial transcript percentage, thus tend to contaminate downstream analysis.

combined_seurat <- subset(combined_seurat, subset = orig.ident != "GSM6376814")

Based on Seurat and Heumos et al, 2023, they simply applied hard threshold on mitochondrial transcript percentage, 5% and 8%, at their quality control step. Additionally, refer to previous Figure 3, I empirically choice the threshold to be 10%.

combined_seurat <- subset(combined_seurat, subset = percent.mt <= 10)

In the end, 2887/24621 = 11.7% cells were removed. Compare with original paper, they removed 9511/24621 = 38.6% cells (Garza et al, 2023). I suppose part of the reason they apply such restrict quality control is they are aware of the low integrity of samples.

# 1. Mito % Plot (After Cleaning)
p_mito_clean <- VlnPlot(
  combined_seurat, 
  features = "percent.mt", 
  group.by = "orig.ident", 
  pt.size = 0
) + 
  vertical_line + 
  ggtitle("Mitochondrial % per Sample (After Cleaning)") + 
  ylab("Mitochondrial Transcript (%)") + 
  scale_y_continuous(limits = c(0, 10)) + # Zoom in since max is now <= 10
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1), 
    legend.position = "none",
    axis.title.x = element_blank()
  ) +
  coord_fixed(ratio = 0.5)

# 2. Total Cells Plot (Your existing code)
meta_data <- combined_seurat@meta.data 
p_cells <- ggplot(meta_data, aes(x = orig.ident, fill = orig.ident)) +
  geom_bar(color = "black") +
  geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Cells per Sample (After Cleaning)", y = "Number of Cells", x = NULL) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none"
  ) +
  coord_fixed(ratio = 0.001) +
  scale_y_continuous(limits = c(0, 4000)) + 
  vertical_line

# 3. Combine them vertically
final_clean_plot <- p_mito_clean / p_cells
final_clean_plot

Figure 5: Number of cells after cleaning grouped by GSMs. The Y-axis indicate number of cells; The X-axis indicate GSMs; The vertical red dash line separate control (left side of red line), and tbi (right side of red line).

Normalization

Differ from RNA-seq and bulk RNA-seq normalization covered in class, sc/snRNA-seq data is zero-inflated, most of gene’s counts is 0 in most of cells. Thus two of the method covered in class, TMM and RLE, are not suitable for sc/snRNA-seq because they both assume most of the gene are not differentially expressed, zero-inflated counts data will yield inflated fold change when ever an algorithm is trying to calculate fold change (Lytal et al, 2020).

Library size based Log-Normalization & filter low expressed gene

The simple normalization by library size still work as intended and won’t affect by inflated zeros and widely utilized in sing-cell transcriptome field as a baseline (Ge et al, 2025, Lytal et al, 2020, Amezquita et al, 2019). In Seurat, it’s wrapped as NormalizeData() function. Note the default scale.factor is set to be 10000 to account for sc/snRNA-seq’s relative small reads per cell compare with bulk RNA-seq.

For visualization, I choice first randomly sample one cell from each GSM and plot the pre and post normalized counts on Y-axis, sampled cell on X-axis. The reason of not plot normalized counts per GSM is because in sc/snRNA-seq, the counts is normalized by the total reads of a cell rather than GSM, thus plot normalized counts per GSM won’t be so meaningful.

In both Seurat tutorial, and Heumos et al, 2023, they used the minimum number of cell expressing certain gene to remove low expressed gene instead of thresholding expression level like in edgeR. Thus I will also filter low expressed gene based on minimum number of cell.

library(tidyr)
library(tibble)

# Collapse the split sample layers into a single unified 'counts' layer
combined_seurat <- JoinLayers(combined_seurat)

# Create a copy of un-normalized data for test purpose
seurat_copy <- combined_seurat

# Randomly sample exactly ONE cell per GSM (orig.ident)
set.seed(42) 
sampled_cells <- combined_seurat@meta.data %>%
  rownames_to_column("Cell_ID") %>%
  group_by(orig.ident) %>%
  sample_n(1) %>%
  pull(Cell_ID)

# Create a mini-Seurat object of just these cells for faster extraction
sc_unfiltered <- subset(combined_seurat, cells = sampled_cells)

# ==========================================
# PLOT 1: Pre-Normalization (Raw Counts)
# ==========================================
raw_matrix <- as.matrix(GetAssayData(sc_unfiltered, layer = "counts"))

df_raw <- as.data.frame(raw_matrix) %>%
  pivot_longer(cols = everything(), names_to = "Cell_ID", values_to = "Count") %>%
  mutate(Sample = sc_unfiltered@meta.data[Cell_ID, "orig.ident"])

p_pre_norm <- ggplot(df_raw, aes(x = Sample, y = Count, fill = Sample)) +
  geom_violin(scale = "width", color = "black", alpha = 0.8) + 
  ggtitle("1. Pre-Normalization: Raw Counts (Unfiltered)") + 
  ylab("Expression (Linear)") +
  theme_classic() +
  theme(axis.text.x = element_blank(), legend.position = "none", axis.title.x = element_blank()) + 
  vertical_line + 
  coord_cartesian(ylim = c(0, 200))


# ==========================================
# PLOT 2: Post-Normalization (Before Filtering)
# ==========================================
# Normalize the main dataset first
combined_seurat <- NormalizeData(combined_seurat, normalization.method = "LogNormalize", scale.factor = 10000)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
# Update our mini-Seurat object to get the new normalized 'data' layer
sc_unfiltered <- subset(combined_seurat, cells = sampled_cells)

norm_matrix_unfilt <- as.matrix(GetAssayData(sc_unfiltered, layer = "data"))

df_norm_unfilt <- as.data.frame(norm_matrix_unfilt) %>%
  pivot_longer(cols = everything(), names_to = "Cell_ID", values_to = "Normalized_Count") %>%
  mutate(Sample = sc_unfiltered@meta.data[Cell_ID, "orig.ident"])

p_post_unfilt <- ggplot(df_norm_unfilt, aes(x = Sample, y = Normalized_Count, fill = Sample)) +
  geom_violin(scale = "width", color = "black", alpha = 0.8) + 
  ggtitle("2. Post-Normalization: Log-Normalized (Unfiltered)") + 
  ylab("Expression (Log1p)") + 
  theme_classic() +
  theme(axis.text.x = element_blank(), legend.position = "none", axis.title.x = element_blank()) + 
  vertical_line + 
  coord_cartesian(ylim = c(0, 6))


# ==========================================
# PLOT 3: Post-Normalization (After Filtering)
# ==========================================
# Calculate frequencies and filter the main dataset
raw_counts_all <- GetAssayData(combined_seurat, layer = "counts")
cells_per_gene <- rowSums(raw_counts_all > 0)
min_cells <- 10
genes_to_keep <- names(which(cells_per_gene >= min_cells))

message("Number of genes BEFORE filtering: ", nrow(raw_counts_all))
message("Number of genes AFTER filtering (>= ", min_cells, " cells): ", length(genes_to_keep))
message("Total genes removed: ", nrow(raw_counts_all) - length(genes_to_keep))

# Subset the main object to remove noisy, low-expressed genes
combined_seurat_filtered <- subset(combined_seurat, features = genes_to_keep)

# Create a new mini-Seurat object based on the FILTERED dataset
sc_filtered <- subset(combined_seurat_filtered, cells = sampled_cells)

norm_matrix_filt <- as.matrix(GetAssayData(sc_filtered, layer = "data"))

df_norm_filt <- as.data.frame(norm_matrix_filt) %>%
  pivot_longer(cols = everything(), names_to = "Cell_ID", values_to = "Normalized_Count") %>%
  filter(Normalized_Count > 0) %>%
  mutate(Sample = sc_filtered@meta.data[Cell_ID, "orig.ident"])

p_post_filt <- ggplot(df_norm_filt, aes(x = Sample, y = Normalized_Count, fill = Sample)) +
  geom_violin(scale = "width", color = "black", alpha = 0.8) + 
  ggtitle(paste0("3. Post-Normalization: Log-Normalized (Filtered >= ", min_cells, " cells)")) + 
  ylab("Expression (Log1p)") + 
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "none", axis.title.x = element_blank()) + 
  vertical_line + 
  coord_cartesian(ylim = c(0, 6))

# ==========================================
# COMBINE PLOTS
# ==========================================
# Stack them vertically 
final_norm_plot <- p_pre_norm / p_post_unfilt / p_post_filt
final_norm_plot

Figure 6: Pre and post normalized counts per cell ( randomly sample 1 cell per GSM). The upper panel indicate the raw counts distribution of the randomly sampled cell; The middle panel indicate the normalized counts of the randomly sampled cell; The bottom panel indicate the distribution of normalized counts after exclude all genes that expressed in less than 10 cells; X-axis indicate the GSMs that cell is sampled from; The vertical red dash line separate control (left side of red line), and tbi (right side of red line).

Before filtering, the raw and normalized count are both inflated at zero due to the sparse nature of sc/snRNA-seq. After filtering, normalized counts yield interesting vases shape, a flat and wide base with numbers of beads attached upon. The flat base is likely because of the zero-inflated nature of sc/snRNA-seq, even though I exclude all gene that expressed in less than 10 cells, most gene still have counts close to zero, following negative binomial distribution. However, I don’t have a well constructed hypothesis to explain those beads attached, maybe because the raw input (raw counts) in discrete, the log-Normalized counts will also be discrete. The relative height of the base is reflecting sequence depth of those cell. When those counts close to zero were normalized by total read of a cell, the larger the total read, the smaller the normalized count. Thus the higher the base, the lower the sequence depth.

Note : X-axis is the GSM those cells sampled from, not GSM itself. So we can’t infer sequence depth of GSM from Figure 5.

Log-CP10k

Since I set the scale.factor as 10000, it’s not per million but per 10k. To calculat the overall statistics of Log-normalized data, first extract expression data using seurat::GetAssayData() then calculate using summary().

Note: apply as.matrix() to extracted expression matrix will convert sparse matrix to dense matrix, which required additional ~4.1GB RAM. Using @x operator will exclude all zero value, but memory efficient

# 1. Extract the normalized sparse matrix
norm_matrix <- GetAssayData(combined_seurat_filtered, layer = "data")

# This summarizes ONLY the non-zero values hidden inside the sparse matrix structure
summary(norm_matrix@x)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.02469 0.60785 1.09461 1.26379 1.77515 8.30359 

The right-skewed property is reflected by small difference between Min, Median, and Mean.

Free memory

# Delete memory pointer from R
rm(combined_seurat)
rm(df_norm_filt)
rm(df_norm_unfilt)
rm(df_raw)
rm(norm_matrix_filt)
rm(norm_matrix_unfilt)
rm(raw_counts_all)
rm(raw_matrix)
rm(sc_filtered)
rm(sc_unfiltered)
rm(meta_data)

# De allocate unused memory
gc()
            used   (Mb) gc trigger   (Mb)   max used   (Mb)
Ncells  10124015  540.7   18916009 1010.3   18916009 1010.3
Vcells 466425547 3558.6  882408323 6732.3 1102761272 8413.5

Z-score Normalization & Dimention reduction

To avoid curse of dimentionality, meaning the distance between every data point is basically the same in high dimension data, dimension reduction is another common step to conduct before differential gene analysis.

First perform Z-score Normalization to linearly shift the mean expression across cells at 0, and standard deviation as 1. This functionally is wrapped in ScaleData() in Seurat. For the sake of memory, only scale the top 2000 gene defined by seurat’s FindVariableFeatures() function.

# Find the top 2000 gene
combined_seurat_filtered <- FindVariableFeatures(
  combined_seurat_filtered, 
  selection.method = "vst", 
  nfeatures = 2000
)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
# Z-score normalization
combined_seurat_filtered <- ScaleData(combined_seurat_filtered)

  |                                                                   
  |                                                             |   0%
  |                                                                   
  |==============================                               |  50%
  |                                                                   
  |=============================================================| 100%
# Create a new mini-Seurat object based on the FILTERED dataset
sc_scaled <- subset(combined_seurat_filtered, cells = sampled_cells)

# 2. Extract Post-Scaling Data (Z-scores)
# Use layer = "scale.data" to pull the matrix you just created
scaled_matrix <- as.matrix(GetAssayData(sc_scaled, layer = "scale.data"))

df_scaled <- as.data.frame(scaled_matrix) %>%
  pivot_longer(cols = everything(), names_to = "Cell_ID", values_to = "Scaled_Expression") %>%
  # NOTE: We do NOT filter > 0 here. Negative values are important Z-scores!
  mutate(Sample = sc_scaled@meta.data[Cell_ID, "orig.ident"])

# 3. Plot the Scaled Distribution
p_scaled <- ggplot(df_scaled, aes(x = Sample, y = Scaled_Expression, fill = Sample)) +
  geom_violin(scale = "width", color = "black", alpha = 0.8) + 
  ggtitle("Post-Scaling: Z-scored Expression (1 Cell per GSM)") + 
  ylab("Expression (Z-score)") + 
  theme_classic() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1), 
    legend.position = "none", 
    axis.title.x = element_blank()
  ) + vertical_line

p_scaled

Figure 7: Expression level after Z-score normalization. Y-axis is the Z-score normalized expression level; X-axis is each single cell sampled from GSM; The vertical red dash line separate control (left side of red line), and tbi (right side of red line).

Then perform principal Component Analysis (PCA), a linear dimension reduction and plot corresponding MDS plot using the first two principle component. The PCA is conduct via seurat’s RunPCA() function. The default parameter crashed R on my machine, so for the sake of performance, only run PCA on the top 2000 variable genes and top 20 PCs.

# Run PCA
combined_seurat_filtered <- RunPCA(combined_seurat_filtered, 
                                   features = VariableFeatures(object = combined_seurat_filtered), 
                                   npcs = 20, 
                                   verbose = TRUE)

# visualize MDS plot
MDS_plot <- DimPlot(combined_seurat_filtered, reduction = "pca", group.by = "Condition")
MDS_plot

Figure 8: MDS plot grouped by condition. Y-axis is the second PC; X-axis is the first PC; Each dot indicate a single cell.

Note control and tbi group are clearly distinguished at four tip branched out from lower left bulk. Which implied transcriptional difference in the cells locate at this region.

Obtain normalized dataframe

NormalizeData()stored normalized counts in data layer. Simply call Seurat::GetAssayData() on data layer to obtain normalized counts, and convert to data.frame use as.data.frame().

After filter low quality cell at cleaning step, there are 21734 cells remain. After filter low expressed gene at normalization step, there are 25389 genes remain. Thus the shape of final resulting expression matrix is 25389 * 21734

# extract normalized data
norm_matrix <- GetAssayData(combined_seurat_filtered, layer = "data")

# Check dimensions (Genes x Cells)
dim(norm_matrix)
[1] 25389 21734
# Check rowname
head(rownames(norm_matrix))
[1] "AL627309.1" "AC114498.1" "AL669831.5" "FAM87B"     "LINC00115" 
[6] "FAM41C"    
# Check colname
head(colnames(norm_matrix))
[1] "GSM6376803_AAACCCAGTTTCGGCG-1" "GSM6376803_AAACGAAAGACCATTC-1"
[3] "GSM6376803_AAACGAACAGAGGTAC-1" "GSM6376803_AAACGAACATGTGGTT-1"
[5] "GSM6376803_AAACGAATCGGATACT-1" "GSM6376803_AAACGAATCTGTCCGT-1"
# convert to data.frame
# Comment out to skip due to memory constrain
# The script below prompt to allocate 5.4 GM of RAM
# norm_df <- as.data.frame(norm_matrix)
# Check dimensions (Genes x Cells)
# dim(norm_df)

The final result is in dgCMatrix instead of data.frame due to memory constrain. Convert to data.frame require additional ~5.4 GB of RAM.

Differential gene expression

Differential gene expression was conduct using Seurat::FindMarkers() function.

Find differentially expressed gene

Refer to original paper (Garza et al, 2023), they use Wilcoxon Rank-Sum test to identify DEs, Bonferroni correction to adjust p_value, and set the adjusted p_value threshold to be 0.01.

# Install presto package for faster Wilcoxon Rank Sum Test
devtools::install_github('immunogenomics/presto')

# Find DEs between contorl and tbi group
Condition_DE_list <- FindMarkers(combined_seurat_filtered, ident.1 = "control", ident.2 = "tbi", group.by = "Condition")

# Calculate the counts by temporarily subsetting the full, unfiltered list
num_pval_05 <- nrow(subset(Condition_DE_list, p_val < 0.05))
num_padj_05 <- nrow(subset(Condition_DE_list, p_val_adj < 0.05))
num_padj_01 <- nrow(subset(Condition_DE_list, p_val_adj < 0.01))

message("Number of genes with raw p_val < 0.05: ", num_pval_05)
message("Number of genes with p_val_adj < 0.05: ", num_padj_05)
message("Number of genes with p_val_adj < 0.01: ", num_padj_01)

# apply threshold
Condition_DE_list <- subset(Condition_DE_list, p_val_adj < 0.01)

# Examin DE LIST
head(Condition_DE_list)

Rows indicate HUGO symbol; Columns indicate:

  • p_val: p value
  • avg_log2FC: average log fold change, positive means upregulated in control
  • pct.1: percentage of cell expressing this gene in control group
  • pct.2:percentage of cell expressing this gene in tbi group
  • p_val_adj: Adjusted p-value, based on bonferroni correction using all genes in the dataset

Volcano plot

library(ggrepel)

# 1. Prepare the dataframe
df_volcano <- Condition_DE_list %>%
  # Move row names to a Gene column
  mutate(Gene = rownames(.)) %>%
  # Prevent -log10(0) from producing infinite values and breaking the plot
  mutate(p_val_adj = ifelse(p_val_adj == 0, .Machine$double.xmin, p_val_adj)) %>%
  
  # Strict p_val_adj < 0.01
  mutate(Significance = case_when(
    p_val_adj < 0.01 & avg_log2FC > 0 ~ "Downregulated in TBI",
    p_val_adj < 0.01 & avg_log2FC < 0 ~ "Upregulated in TBI",
    TRUE ~ "Not Significant"
  ))

# 2. Extract the top 10 Upregulated and top 10 Downregulated genes to label
# (Still finding the extremes based on Fold Change, but pulling from the new Significance groups)
top_up <- df_volcano %>% filter(Significance == "Downregulated in TBI") %>% top_n(10, wt = avg_log2FC)
top_down <- df_volcano %>% filter(Significance == "Upregulated in TBI") %>% top_n(-10, wt = avg_log2FC)
top_genes_to_label <- bind_rows(top_up, top_down)

# 3. Generate the Volcano Plot 
volcano_plot <- ggplot(df_volcano, aes(x = avg_log2FC, y = -log10(p_val_adj), color = Significance)) +
  geom_point(alpha = 0.6, size = 1.5) +
  # Custom colors
  scale_color_manual(values = c(
    "Upregulated in TBI" = "#0072B2", 
    "Downregulated in TBI" = "#D55E00",
    "Not Significant" = "grey80"
  )) +
  # Add labels only for the top genes
  geom_text_repel(data = top_genes_to_label, aes(label = Gene), 
                  size = 3.5, box.padding = 0.5, max.overlaps = Inf, color = "black") +
                  
  # UPDATED THRESHOLD LINES: 
  # Horizontal line at 0.01 p-value cutoff
  geom_hline(yintercept = -log10(0.01), linetype = "dashed", color = "black") +
  # Single vertical line at 0 (since magnitude no longer matters)
  geom_vline(xintercept = 0, linetype = "dashed", color = "black") +
  
  theme_classic() +
  ggtitle("Volcano Plot: TBI vs. Control (p_val_adj < 0.01)") +
  xlab("Average Log2 Fold Change") +
  ylab("-Log10(Adjusted P-value)") +
  theme(legend.position = "bottom", 
        axis.text.y = element_text(size = 14), 
    
    # Increases the size of the Legend Title ("Expression", "Identity")
    legend.title = element_text(size = 14),
    
    # Increases the size of the Legend Text (the numbers and group names)
    legend.text = element_text(size = 14),
    
    # Increases the size of the Main Title
    plot.title = element_text(size = 16))

volcano_plot

Figure 9: MDS plot grouped by condition. Y-axis is negative log-transformed adjusted p value; X-axis is the average log fold change centered at zero; Blue dots on the left indicate genes that downredulated in tbi group; Orange bots on the right indicate genes that upregulated in tbi group; The horizontal black dash line indicate adjusted p value = 0.01 threshold. 10 most up and down regulated gene (juedge by the larger absolute average log fold change) are tagged by their HUGO symbol.

The adjusted p value of downregulated gene is much higher than downregulated gene, bu the reason remain unknown. Interestingly, the left most upregulate gene XIST is known to only express in female, observing XIST as upregulate gene in tbi group reflect the biased sex compisition between control (all 5 sample are male) and tbi ( 2 out of 11 are female) group.

Heatmap

The top 20 gene, defined by the lowest adjusted p value ware plotted as heatmap.

# 1. Identify the top 20 most significant genes (lowest p_val_adj)
top20_genes <- df_volcano %>%
  arrange(p_val_adj) %>% # Sort from smallest p-value to largest
  head(20) %>%           # Grab the top 20 rows
  pull(Gene)             # Extract just the gene names

# 2. Memory-Safe Scaling
# Only scale these 20 specific genes across the cells so R doesn't crash!
combined_seurat_filtered <- ScaleData(combined_seurat_filtered, features = top20_genes)

  |                                                                   
  |                                                             |   0%
  |                                                                   
  |=============================================================| 100%
# 3. Generate the Heatmap
heatmap_plot <- DoHeatmap(
  object = combined_seurat_filtered,
  features = top20_genes,
  group.by = "Condition", # This separates the cells into TBI and Control blocks
  size = 4,               # Size of the top group labels
  angle = 45              # Angle of the group labels
) + 
  ggtitle("Heatmap of Top 20 Differentially Expressed Genes") +
  # Adjust the Y-axis text size so the gene names are readable
  theme(axis.text.y = element_text(size = 14))

heatmap_plot

Figure 10: Heatmap of top 20 gene with lowest adjusted p value. Y-axis indicate the HOGO symbol of selected genes; Each timy column along X-asix is a cell; The left half indicate their expression level in control group; The right half indicate their expression level in tbi group.

The heatmap doesn’t show clear cluster between control and tbi group. There is minor brightness difference with control group being darker, indicate overall higher expression level in control group. One hypothesis is that samples in tbi group are more likely to loss cell integraty and experience RNA leakage result from impact damage.

Conclustion

This project utilized dataset GSE209552 to examine potential transcriptome difference between non-neurological deaths (control) and Traumatic brain injury (tbi) at single cell resolution. Subpopulation difference between control and tbi group was observed, revealed by MDS plot, where tbi group tend to concentrate on four tips brach out from lower-left bulk. 12223 differentially expressed gene were identified by calling seurat:FindMarker() indicate the existance of difference between control and tbi group. However, there is not enough evidence to infer the functional and/or pathway difference among contorl and tbi group.

Question

  1. Why is the dataset of interest to you?
  1. What are the control and test conditions of the dataset?
  1. How many samples in each of the conditions of your dataset?
  1. Were there expression values that were not unique for specific genes? How did you handle these?
  1. Were there expression values that could not be mapped to current HUGO symbols?
  1. Were there any outliers in your dataset? How were they handled in the originating paper? How many outliers were removed?
  1. How did you handle replicates?
  1. What is the final coverage of your dataset?
  1. Which normalization method did you use and why?
  1. How many genes were significantly differentially expressed? What thresholds did you use and why?
  1. Which method did you use to adjust p value? And Why? How many genes passed correction?
  1. Do you conditions cluster together at heatmap? Explain why or why not.

References

“A Grammar of Data Manipulation.” n.d. Accessed February 13, 2026. https://dplyr.tidyverse.org/index.html.
Amezquita, Robert A., Aaron T. L. Lun, Etienne Becht, et al. 2020. “Orchestrating Single-Cell Analysis with Bioconductor.” Nature Methods 17 (2): 137–45. https://doi.org/10.1038/s41592-019-0654-x.
“Analysis, Visualization, and Integration of Visium HD Spatial Datasets with Seurat.” n.d. Accessed February 7, 2026. https://satijalab.org/seurat/articles/pbmc3k_tutorial.
Bakken, Trygve E., Rebecca D. Hodge, Jeremy A. Miller, et al. 2018. “Single-Nucleus and Single-Cell Transcriptomes Compared in Matched Cortical Cell Types.” PLoS ONE 13 (12): e0209648. https://doi.org/10.1371/journal.pone.0209648.
Chapter 19 Single-Nuclei RNA-Seq Processing Orchestrating Single-Cell Analysis with Bioconductor. n.d. Accessed February 12, 2026. https://github.com/Bioconductor/OrchestratingSingleCellAnalysis.
“Create Elegant Data Visualisations Using the Grammar of Graphics.” n.d. Accessed February 13, 2026. https://ggplot2.tidyverse.org/.
Garza, Raquel, Yogita Sharma, Diahann A. M. Atacho, et al. 2023. “Single-Cell Transcriptomics of Human Traumatic Brain Injury Reveals Activation of Endogenous Retroviruses in Oligodendroglia.” Cell Reports 42 (11). https://doi.org/10.1016/j.celrep.2023.113395.
Ge, Qinyu, Yuqi Sheng, Junru Lu, Yuwei Yang, and Min Pan. 2025. “Single-Cell RNA-Seq Data Normalization: A Benchmarking Study.” PLOS ONE 20 (12): e0335102. https://doi.org/10.1371/journal.pone.0335102.
GEOmetadb. Bioconductor.” n.d. Accessed February 6, 2026. http://bioconductor.org/packages/GEOmetadb/.
Hao, Yuhan, Tim Stuart, Madeline H. Kowalski, et al. 2024. “Dictionary Learning for Integrative, Multimodal and Scalable Single-Cell Analysis.” Nature Biotechnology 42 (2): 293–304. https://doi.org/10.1038/s41587-023-01767-y.
Heumos, Lukas, Anna C. Schaar, Christopher Lance, et al. 2023. “Best Practices for Single-Cell Analysis Across Modalities.” Nature Reviews Genetics 24 (8): 550–72. https://doi.org/10.1038/s41576-023-00586-w.
Isserlin, BCB420 / Ruth. n.d. Differential Expression Tutorial. Accessed February 13, 2026. https://bcb420-2026.github.io/Example_Student/differential_expression/.
Isserlin, Ruth. n.d.-a. Finding Expression Data with GEOmetadb. Accessed February 13, 2026. https://bcb420-2026.github.io/Example_Student/using_geometadb/.
Isserlin, Ruth. n.d.-b. Normalization (BCB420 Lecture). Accessed February 13, 2026. https://bcb420-2026.github.io/Example_Student/normalization/.
Isserlin, Ruth. n.d.-c. Worked Example. Accessed February 13, 2026. https://bcb420-2026.github.io/Example_Student/id_mapping/.
Lytal, Nicholas, Di Ran, and Lingling An. 2020. “Normalization Methods on Single-Cell RNA-Seq Data: An Empirical Survey.” Frontiers in Genetics 11 (February). https://doi.org/10.3389/fgene.2020.00041.
“Org.hs.eg.db. Bioconductor.” n.d. Accessed February 13, 2026. http://bioconductor.org/packages/org.Hs.eg.db/.
r-dbi. 2026. R-Dbi/RSQLite. Released February 12. https://github.com/r-dbi/RSQLite.
Slowikowski, Kamil, Alicia Schep, Sean Hughes, et al. 2024. Ggrepel: Automatically Position Non-Overlapping Text Labels with ’Ggplot2’. V. 0.9.6. Released September 7. https://cran.r-project.org/web/packages/ggrepel/index.html.
Wälchli, Thomas, Moheb Ghobrial, Marc Schwab, et al. 2024. “Single-Cell Atlas of the Human Brain Vasculature Across Development, Adulthood and Disease.” Nature 632 (8025): 603–13. https://doi.org/10.1038/s41586-024-07493-y.
LS0tCnRpdGxlOiAiQXNzaWdubWVudCAjMSIKYXV0aG9yOiAiSmlhcWkgTWEiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIGNvZGVfZm9sZGluZzogaGlkZSAgCiAgICB0aGVtZTogam91cm5hbAogICAgZmlnLmNhcHRpb246IHRydWUKICAgIHRvYzogdHJ1ZSAgICAgICAgICAgICAgICAgIAogICAgdG9jX2RlcHRoOiAzICAgICAgICAgICAgICAgIAogICAgdG9jX2Zsb2F0OiB0cnVlCmJpYmxpb2dyYXBoeTogQkNCNDIwLmJpYgpsaW5rLWNpdGF0aW9uczogdHJ1ZQpub2NpdGU6ICdAKicKLS0tCgpgYGB7PWh0bWx9CjxzdHlsZT4KYm9keSB7CiAgZm9udC1zaXplOiAxNnB4Owp9CgpjYXB0aW9uIHsKICBmb250LXNpemU6IDE0cHg7CiAgZm9udC1zdHlsZTogaXRhbGljOwp9Cjwvc3R5bGU+CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIGVjaG89RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgIyBzdXBwcmVzc2luZyB3YXJuaW5nIG1lc3NhZ2VzCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBrbml0ci5rYWJsZS5tYXhfcm93cyA9IDUpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRyYXVtYXRpYyBicmFpbiBpbmp1cnkgKFRCSSkgZnJlcXVlbnRseSBsZWFkcyB0byBsYXN0aW5nIG5ldXJvbG9naWNhbCBkZWZpY2l0cywgZHJpdmVuIGxhcmdlbHkgYnkgYSBzdHJvbmcgeWV0IHVucmVzb2x2ZWQgbm9uaW5mbGFtbWF0b3J5IHJlYWN0aW9uLiBUbyBiZXR0ZXIgdW5kZXJzdGFuZCB0aGlzIHByb2Nlc3MsIEkgcmUtYW5hbHl6ZWQgdGhlIHB1Ymxpc2hlZCBgc25STkEtc2VxYCBkYXRhc2V0IGBHU0UyMDk1NTJgIFtHYXJ6ZSBldCBhbCwgMjAyM10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jZWxyZXAuMjAyMy4xMTMzOTUpLCB0byB0YWtlIGFkdmFudGFnZSBvZiB0aGVpciBzaW5nbGUgY2VsbCByZXNvbHV0aW9uLgoKIVtGaWd1cmUgMTogRGF0YSBvdmVydmlldyAoR2FyemEgZXQgYWwsIDIwMjM7IFN1cHBsZW1lbnRhbCB0YWJsZSAxKV0oRGF0YV9kZXNjcmlwdGlvbi5wbmcpCgojIERhdGEgRG93bmxvYWQgeyNkYXRhLWRvd25sb2FkfQoKLSAgIE15IGNob2ljZSBvZiBkYXRhc2V0IGlzIGBHU0UyMDk1NTJgLgotICAgQ29udHJvbDogbm9uLW5ldXJvbmFsIGRlYXRoIHBvc3Rtb3J0ZW0gYnJhaW4KLSAgIFRyZWF0bWVudDogVHJhdW1hdGljIGJyYWluIGluanVyeSAoVEJJKQotICAgTm90ZTogYEdTRTIwOTU1MmAgaXMgc2luZ2xlIG51Y2xldXMgUk5BLXNlcSAoYHNuUk5BLXNlcWApLCBhIHZhcmlhbnQgb2YgYHNjUk5BLXNlcWAgdGhhdCBvbmx5IHNlcXVlbmNlIG51Y2xldXMgYFJOQWAuCi0gICBOb3RlOiBUaGUgc2NyaXB0cyBiZWxvdyBmcmVxdWVudGx5IGNyYXNoZWQgUiBkdWUgdG8gbWVtb3J5IGNvbnN0cmFpbiBvbiBteSBsYXB0b3AgKDE2R0IpLiBJIGFudGljaXBhdGUgMzJHQiBvZiBSQU0gaXMgbmVlZGVkIHRvIHNhZmVseSBydW4gc2NyaXB0cyBiZWxvdy4KCmBgYHtyfQojIERlZmluZSBnbG9iYWwgY29uc3RhbnQKR1NFX05VTSA8LSAiR1NFMjA5NTUyIgpgYGAKCiMjIEdFT21ldGFkYiBzZXR1cAoKVG8gc2V0dXAgSSBmb2xsb3dlZCBbR0VPbWV0YWRiIFR1dG9yaWFsIENoYXB0ZXIgM10oaHR0cHM6Ly9iY2I0MjAtMjAyNi5naXRodWIuaW8vRXhlcmNpc2VfRmluZGluZ19FeHByZXNzaW9uX2RhdGEvc2V0dGluZy11cC1nZW9tZXRhZGIuaHRtbCkuCgpgYGB7cn0KIyBJbnN0YWxsIHJlcXVpcmVkIHBhY2thZ2UgaWYgbm90IGluc3RhbGxlZAppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJHRU9tZXRhZGIiLCBxdWlldGx5ID0gVFJVRSkpCiAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkdFT21ldGFkYiIpCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiREJJIiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoIkRCSSIpCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiUlNRTGl0ZSIsIHF1aWV0bHkgPSBUUlVFKSkKICBpbnN0YWxsLnBhY2thZ2VzKCJSU1FMaXRlIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJkcGx5ciIsIHF1aWV0bHkgPSBUUlVFKSkKICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiZ2dwbG90MiIsIHF1aWV0bHkgPSBUUlVFKSkKICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKCiMgQXR0YWNoIHBhY2thZ2VzCmxpYnJhcnkoR0VPbWV0YWRiKQpsaWJyYXJ5KERCSSkKbGlicmFyeShSU1FMaXRlKQoKIyBEb3dubG9hZCBTUUxpdGUgZGF0YWJhc2UgaWYgbm90IGV4aXN0CmlmICghZmlsZS5leGlzdHMoJ0dFT21ldGFkYi5zcWxpdGUnKSkgZ2V0U1FMaXRlRmlsZSgpCmZpbGUuaW5mbygnR0VPbWV0YWRiLnNxbGl0ZScpCgojIENvbm5lY3QgdG8gZGF0YWJhc2UKY29uIDwtIGRiQ29ubmVjdChTUUxpdGUoKSwgJ0dFT21ldGFkYi5zcWxpdGUnKQpgYGAKCiMjIERvd25sb2FkIHdob2xlIEdTRTIwOTU1MgoKSSBmb2xsb3dlZCBbSWRlbnRpZmllciBNYXBwaW5nIFR1dG9yaWFsXShodHRwczovL2JjYjQyMC0yMDI2LmdpdGh1Yi5pby9FeGFtcGxlX1N0dWRlbnQvaWRfbWFwcGluZy9nZXR0aW5nLWRhdGEtZnJvbS1nZW8uaHRtbCkuIFRoZSBoZWxwZXIgZnVuY3Rpb24gZGVmaW5lZCBiZWxvdyBpcyBtb2RpZmllZCBiYXNlZCBvbiBbSWRlbnRpZmllciBNYXBwaW5nIFR1dG9yaWFsJ3MgaGVscGVyIGZ1bmN0aW9uXShodHRwczovL2dpdGh1Yi5jb20vYmNiNDIwLTIwMjYvRXhhbXBsZV9TdHVkZW50L2Jsb2IvbWFpbi9pbl9jbGFzc19leGVyY2lzZXMvaWRfbWFwcGluZy9mZXRjaF9nZW9fc3VwcC5SKSB0byBhdm9pZCBkdXBsaWNhdGVkIGRvd25sb2FkLgoKYGBge3J9CiMgRGVmaW5lIGhlbHBlciBmdW5jdGlvbgpmZXRjaF9nZW9fc3VwcCA8LSBmdW5jdGlvbihnc2UsIGRlc3RkaXIgPSAiZGF0YSIpIHsKICAjIENhbGN1bGF0ZSB0aGUgYWN0dWFsIHBhdGggd2hlcmUgR0VPcXVlcnkgcHV0cyB0aGUgZmlsZXMKICB0YXJnZXRfcGF0aCA8LSBmaWxlLnBhdGgoZGVzdGRpciwgZ3NlKQogIAogICMgQ2hlY2sgaWYgZGlyZWN0b3J5IGV4aXN0cyBBTkQgY29udGFpbnMgZmlsZXMKICBpZiAoZGlyLmV4aXN0cyh0YXJnZXRfcGF0aCkgJiYgbGVuZ3RoKGxpc3QuZmlsZXModGFyZ2V0X3BhdGgpKSA+IDApIHsKICAgIG1lc3NhZ2UocGFzdGUoIkRhdGEgZm91bmQgYXQiLCB0YXJnZXRfcGF0aCwgIi0gU2tpcHBpbmcgZG93bmxvYWQuIikpCiAgICByZXR1cm4oaW52aXNpYmxlKHRhcmdldF9wYXRoKSkKICB9CiAgCiAgIyBJZiBtaXNzaW5nIG9yIGVtcHR5LCBwcm9jZWVkIHdpdGggZG93bmxvYWQKICBtZXNzYWdlKHBhc3RlKCJEb3dubG9hZGluZyIsIGdzZSwgInRvIiwgZGVzdGRpciwgIi4uLiIpKQogIGRpci5jcmVhdGUoZGVzdGRpciwgc2hvd1dhcm5pbmdzID0gRkFMU0UsIHJlY3Vyc2l2ZSA9IFRSVUUpCiAgR0VPcXVlcnk6OmdldEdFT1N1cHBGaWxlcyhHRU8gPSBnc2UsIGJhc2VEaXIgPSBkZXN0ZGlyLCBtYWtlRGlyZWN0b3J5ID0gVFJVRSkKICAKICBpbnZpc2libGUodGFyZ2V0X3BhdGgpCn0KCiMgRG93bmxvYWQKZmV0Y2hfZ2VvX3N1cHAoZ3NlID0gR1NFX05VTSkKYGBgCgojIyBDb25zdHJ1Y3Qgc25STkEtc2VxIG9ubHkgcmVmZXJlbmNlIGxpc3QKClRoaXMgR1NFIGNvbnRhaW4gYm90aCBgc25STkEtc2VxYCBhbmQgYGJ1bGsgUk5BLXNlcWAgc2FtcGxlLiBSZW1vdmFsIG9mIGJ1bGsgUk5BLXNlcSBpcyBuZWVkZWQgdG8gYXZvaWQgc2lnbmlmaWNhbnQgYmF0Y2ggZWZmZWN0IGR1ZSB0byB0aGVpciB0ZWNobmljYWwgZGlmZmVyZW5jZS4gQWx0aG91Z2ggdGhlIG51bWJlciBvZiBzYW1wbGVzIGlzIHJlZHVjZWQgdG8gMTcsIGl0J3Mgc3RpbGwgc3VmZmljaWVudCBhbW91bnQuCgpSZWZlciB0byB0aGUgb3JpZ2luYWwgcGFwZXIncyBtZXRob2Qgc2VjdGlvbiAoW0dhcnphIGV0IGFsLCAyMDIzXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNlbHJlcC4yMDIzLjExMzM5NSkpLCBvbmx5IHRoZSBgc25STkEtc2VxYCB3YXMgZG9uZSBieSBgSWxsdW1pbmEgTmV4dFNlcTYwMDBgIHBsYXRmb3JtLCBbR1BMMjQ2NzZdKGh0dHBzOi8vd3d3LW5jYmktbmxtLW5paC1nb3YubXlhY2Nlc3MubGlicmFyeS51dG9yb250by5jYS9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1BMMjQ2NzYpLiBJIGNhbiB1c2UgdGhpcyBjbHVlIHRvIGV4Y2x1ZGUgYWxsIGBidWxrIFJOQS1zZXFgLgoKU2luY2UgYWxsIEdTTXMgb2YgdGhpcyBzdHVkeSBhcmUgcGFja2VkIGluIG9uZSBgR1NFMjA5NTUyX1JBVy50YXJgIGZpbGUgaW4gR0VPLCBJIGNhbid0IGluZGljYXRlIHdoaWNoIHBsYXRmb3JtIHRvIGluY2x1ZGUvZXhjbHVkZSBhdCBkb3dubG9hZCBzdGVwLiBUaHVzIEkgY29uc3RydWN0ZWQgYSBsaXN0IG9mIGBHUEwyNDY3NmAgR1NNcyBmb3IgZG93bnN0cmVhbSByZWZlcmVuY2UgYnkgcXVlcnkgYEdFT21ldGFiZGAuCgpgYGB7cn0KIyBEZWZpbmUgZGVzaXJlZCBwbGF0Zm9ybQpzblJOQV9wbGF0Zm9ybSA8LSAiR1BMMjQ2NzYiCgojIENvbnN0cnVjdCB0aGUgcXVlcnkKIyBXZSBqb2luIHRoZSAnbGluaycgdGFibGUgKGdzZV9nc20pIHdpdGggdGhlICdzYW1wbGUnIHRhYmxlIChnc20pIAojIHRvIGNoZWNrIHRoZSBwbGF0Zm9ybSAoZ3BsKSBjb2x1bW4uCnNxbF9maWx0ZXIgPC0gcGFzdGUwKAogICJTRUxFQ1QgdDEuZ3NtICIsCiAgIkZST00gZ3NlX2dzbSB0MSAiLAogICJKT0lOIGdzbSB0MiBPTiB0MS5nc20gPSB0Mi5nc20gIiwKICAiV0hFUkUgdDEuZ3NlID0gJyIsIEdTRV9OVU0sICInICIsCiAgIkFORCB0Mi5ncGwgPSAnIiwgc25STkFfcGxhdGZvcm0sICInIgopCgojIEdldCB0aGUgbGlzdCBvZiBwdXJlIHNuUk5BLXNlcSBzYW1wbGVzCnNuX2dzbV9saXN0IDwtIGRiR2V0UXVlcnkoY29uLCBzcWxfZmlsdGVyKSRnc20KCiMgVmlldyByZXN1bHRzCnByaW50KHBhc3RlKCJGb3VuZCIsIGxlbmd0aChzbl9nc21fbGlzdCksICJzblJOQS1zZXEgc2FtcGxlcy4iKSkKc25fZ3NtX2xpc3QKYGBgCgojIEFjY2VzcyBkYXRhIHF1YWxpdHkKCi0gICBUaGUgZm9sbG93aW5nIGFuYWx5c2lzIGlzIGJ1aWxkIG9uIFtTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKSBmcmFtZXdvcmsgKFtIYW8gZXQgYWwsIDIwMjNdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L3M0MTU4Ny0wMjMtMDE3NjcteSkpIGFuZCByZWZlcnJpbmcgW0hldW1vcyBldCBhbCwgMjAyM10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvczQxNTc2LTAyMy0wMDU4Ni13KSBmb3IgaGlnaC1sZXZlbCBpZGVhLgoKYGBge3J9CiMgSW5zdGFsbCBTZXVyYXQgaWYgbm90CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiU2V1cmF0IiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoIlNldXJhdCIpCmBgYAoKIyMgUmVvcmdhbml6ZSBzblJOQS1zZXEgZmlsZXMKClRoZSByYXcgY291bnQgZGF0YSB3YXMgc3RvcmVkIGluIGBHU0UyMDk1NTJfUkFXLnRhcmAgZmlsZSwgdGh1cyBuZWVkIHRvIGJlIHVucGFja2VkIHVzaW5nIGB1bnRhcigpYCBmdW5jdGlvbi4KCmBgYHtyfQojIEF0dGFjaCBwYWNrYWdlcwpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShkcGx5cikKbGlicmFyeShzdHJpbmdyKQoKIyBEZWZpbmUgZGF0YSBwYXRocwp0YXJfZmlsZSA8LSAiZGF0YS9HU0UyMDk1NTIvR1NFMjA5NTUyX1JBVy50YXIiCmV4dHJhY3RfZGlyIDwtICJkYXRhL0dTRTIwOTU1Mi9yYXdfZmlsZXMiCgojIERlZmluZSB1bnBhY2sgaGVscGVyIGZ1bmN0aW9uCnVucGFja19nZW9fdGFyIDwtIGZ1bmN0aW9uKHRhcl9wYXRoLCBkZXN0X2RpcikgewogIGlmIChkaXIuZXhpc3RzKGRlc3RfZGlyKSAmJiBsZW5ndGgobGlzdC5maWxlcyhkZXN0X2RpcikpID4gMCkgewogICAgbWVzc2FnZShwYXN0ZSgi8J+TgiBFeHRyYWN0ZWQgZmlsZXMgZm91bmQgYXQiLCBkZXN0X2RpciwgIi0gU2tpcHBpbmcgdW5wYWNrLiIpKQogICAgcmV0dXJuKGludmlzaWJsZShkZXN0X2RpcikpCiAgfQogIGlmICghZmlsZS5leGlzdHModGFyX3BhdGgpKSB7CiAgICBzdG9wKHBhc3RlKCLinYwgVGFyIGZpbGUgbm90IGZvdW5kOiIsIHRhcl9wYXRoKSkKICB9CiAgbWVzc2FnZShwYXN0ZSgi8J+TpiBVbnBhY2tpbmciLCBiYXNlbmFtZSh0YXJfcGF0aCksICIuLi4iKSkKICBkaXIuY3JlYXRlKGRlc3RfZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSwgcmVjdXJzaXZlID0gVFJVRSkKICB1bnRhcih0YXJfcGF0aCwgZXhkaXIgPSBkZXN0X2RpcikKICBtZXNzYWdlKCLinIUgVW5wYWNraW5nIGNvbXBsZXRlLiIpCiAgcmV0dXJuKGludmlzaWJsZShkZXN0X2RpcikpCn0KCiMgUnVuIHRoZSB1bnBhY2tlcgp1bnBhY2tfZ2VvX3Rhcih0YXJfZmlsZSwgZXh0cmFjdF9kaXIpCmBgYAoKVGhlIHVucGFja2VkIGZpbGUgaXMgdmVyeSBtZXNzeSwgY29udGFpbiBhbGwgMTB4IGdlbm9taWNzIGBzblJOQS1zZXFgIGZpbGVzLCBgYnVsayBSTkEtc2VxYCBmaWxlcywgc3Vic2V0dGVkIFRyYW5zcG9zYWJsZSBFbGVtZW50cyBmaWxlLCBjbHVzdGVyZWQgZmlsZXMsIGFuZCBub3JtYWxpemVkIGZpbGVzLiBBbW9uZyB0aG9zZSwgb25seSAxMHggZ2Vub21pY3MgYHNuUk5BLXNlcWAgaXMgd2hhdCBJIG5lZWQuCgpUbyBleHRyYWN0LCBJIHV0aWxpemUgcHJldmlvdXNseSBkZWZpbmVkIGBzbl9nc21fbGlzdGAsIGxvb3Agb3ZlciBpdCwgZXhjbHVkZSBhbnkgZmlsZSBjb250YWluIGBfVEVfYCwgaW5kaWNhdGUgVHJhbnNwb3NhYmxlIEVsZW1lbnRzLCBvbmx5IGV4dHJhY3QgZmlsZXMgZW5kIHdpdGggZWl0aGVyIGBiYXJjb2RlYCwgYG1hdHJpeGAsIG9yIGBmZWF0dXJlc2AuIExhc3RseSwgdHJpbSB0aGUgZmlsZSBuYW1lIHRvIGBiYXJjb2RlYCwgYG1hdHJpeGAsIGBmZWF0dXJlc2Agb25seSwgdG8gbWVldCB0aGUgZXhwZWN0ZWQgbmFtaW5nIGZvcm1hdCBvZiBbU2V1cmF0XShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0LykncyBgUmVhZDEwWCgpYCBmdW5jdGlvbi4KCmBgYHtyfQojIFJlb3JnYW5pemUgYWxsIHNuUk5BLXNlcSAxMHggZmlsZXMgaW4gdG8gYSBuZXcgZGlyZWN0b3J5CmRlc3RfZGlyIDwtICJkYXRhL0dTRTIwOTU1Mi8xMHhfb3JnYW5pemVkIiAgICAjIFdoZXJlIHRoZXkgc2hvdWxkIGdvCmRpci5jcmVhdGUoZGVzdF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQoKIyAtLS0gTUFJTiBMT09QIC0tLQptZXNzYWdlKHBhc3RlKCLwn5qAIE9yZ2FuaXppbmcgZmlsZXMgZm9yIiwgbGVuZ3RoKHNuX2dzbV9saXN0KSwgInNhbXBsZXMuLi4iKSkKCnNraXBwZWRfY291bnQgPC0gMAptb3ZlZF9jb3VudCAgIDwtIDAKCmZvciAoZ3NtIGluIHNuX2dzbV9saXN0KSB7CiAgCiAgIyAxLiBEZWZpbmUgdGhlIHNwZWNpZmljIGRlc3RpbmF0aW9uIHBhdGggZm9yIHRoaXMgc2FtcGxlCiAgc2FtcGxlX3N1YmRpciA8LSBmaWxlLnBhdGgoZGVzdF9kaXIsIGdzbSkKICAKICAjIDIuIENIRUNLOiBEb2VzIGl0IGFscmVhZHkgZXhpc3Q/CiAgaWYgKGRpci5leGlzdHMoc2FtcGxlX3N1YmRpcikgJiYgbGVuZ3RoKGxpc3QuZmlsZXMoc2FtcGxlX3N1YmRpcikpID49IDMpIHsKICAgIHNraXBwZWRfY291bnQgPC0gc2tpcHBlZF9jb3VudCArIDEKICAgIG5leHQKICB9CiAgCiAgIyAzLiBDcmVhdGUgZGlyZWN0b3J5CiAgZGlyLmNyZWF0ZShzYW1wbGVfc3ViZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKICAKICAjIDQuIEZpbmQgc291cmNlIGZpbGVzIChFeGNsdWRlIHRoZSAnVEUnIGZpbGVzKQogIHBhdHRlcm4gPC0gcGFzdGUwKCJeIiwgZ3NtKQogIGZpbGVzX3RvX21vdmUgPC0gbGlzdC5maWxlcyhleHRyYWN0X2RpciwgcGF0dGVybiA9IHBhdHRlcm4sIGZ1bGwubmFtZXMgPSBUUlVFKQogIGZpbGVzX3RvX21vdmUgPC0gZmlsZXNfdG9fbW92ZVshZ3JlcGwoIl9URV8iLCBmaWxlc190b19tb3ZlKV0KICAKICAjIFNhZmV0eSBDaGVja3MKICBpZiAobGVuZ3RoKGZpbGVzX3RvX21vdmUpID09IDApIHsKICAgIHdhcm5pbmcocGFzdGUoIuKaoO+4jyBTa2lwcGluZyIsIGdzbSwgIi0gTm8gc291cmNlIGZpbGVzIGZvdW5kLiIpKQogICAgbmV4dAogIH0KICAKICAjIDUuIE1PVkUgJiBSRU5BTUUKICAjIFdlIGlkZW50aWZ5IHNwZWNpZmljIGZpbGVzIHRvIGVuc3VyZSB3ZSByZW5hbWUgdGhlbSBjb3JyZWN0bHkgZm9yIFNldXJhdAogIGZfbWF0cml4ICAgPC0gZmlsZXNfdG9fbW92ZVtncmVwKCJtYXRyaXgiLCBmaWxlc190b19tb3ZlLCBpZ25vcmUuY2FzZSA9IFRSVUUpXQogIGZfYmFyY29kZXMgPC0gZmlsZXNfdG9fbW92ZVtncmVwKCJiYXJjb2RlcyIsIGZpbGVzX3RvX21vdmUsIGlnbm9yZS5jYXNlID0gVFJVRSldCiAgZl9mZWF0dXJlcyA8LSBmaWxlc190b19tb3ZlW2dyZXAoImZlYXR1cmVzfGdlbmVzIiwgZmlsZXNfdG9fbW92ZSwgaWdub3JlLmNhc2UgPSBUUlVFKV0KICAKICAjIFZlcmlmeSB3ZSBmb3VuZCBleGFjdGx5IDEgb2YgZWFjaCBiZWZvcmUgbW92aW5nCiAgaWYgKGxlbmd0aChmX21hdHJpeCkgPT0gMSAmJiBsZW5ndGgoZl9iYXJjb2RlcykgPT0gMSAmJiBsZW5ndGgoZl9mZWF0dXJlcykgPT0gMSkgewogICAgCiAgICAjIFJlbmFtZSBNYXRyaXggLT4gbWF0cml4Lm10eC5negogICAgZmlsZS5yZW5hbWUoZl9tYXRyaXgsIGZpbGUucGF0aChzYW1wbGVfc3ViZGlyLCAibWF0cml4Lm10eC5neiIpKQogICAgCiAgICAjIFJlbmFtZSBCYXJjb2RlcyAtPiBiYXJjb2Rlcy50c3YuZ3oKICAgIGZpbGUucmVuYW1lKGZfYmFyY29kZXMsIGZpbGUucGF0aChzYW1wbGVfc3ViZGlyLCAiYmFyY29kZXMudHN2Lmd6IikpCiAgICAKICAgICMgUmVuYW1lIEdlbmVzL0ZlYXR1cmVzIC0+IGZlYXR1cmVzLnRzdi5negogICAgZmlsZS5yZW5hbWUoZl9mZWF0dXJlcywgZmlsZS5wYXRoKHNhbXBsZV9zdWJkaXIsICJmZWF0dXJlcy50c3YuZ3oiKSkKICAgIAogICAgbWVzc2FnZShwYXN0ZSgiICDinIUgU3RhbmRhcmRpemVkICYgTW92ZWQ6IiwgZ3NtKSkKICAgIG1vdmVkX2NvdW50IDwtIG1vdmVkX2NvdW50ICsgMQogICAgCiAgfSBlbHNlIHsKICAgIHdhcm5pbmcocGFzdGUoIiAg4pqg77iPIFNraXBwaW5nIiwgZ3NtLCAiLSBDb3VsZCBub3QgZmluZCB1bmlxdWUgMTB4IHRyaXBsZXQgdG8gc3RhbmRhcmRpemUuIikpCiAgfQp9CgojIC0tLSBTVU1NQVJZIC0tLQptZXNzYWdlKCLwn46JIE9yZ2FuaXphdGlvbiBDb21wbGV0ZS4iKQptZXNzYWdlKHBhc3RlKCIgIPCflLkgU2FtcGxlcyBtb3ZlZCAmIHN0YW5kYXJkaXplZDoiLCBtb3ZlZF9jb3VudCkpCm1lc3NhZ2UocGFzdGUoIiAg8J+UuCBTYW1wbGVzIHNraXBwZWQgKGFscmVhZHkgZG9uZSk6Iiwgc2tpcHBlZF9jb3VudCkpCmBgYAoKIyMgTG9hZCBzblJOQS1zZXEgZmlsZXMgaW50byBSIHVzaW5nIFNldXJhdAoKTG9hZGluZyBvZiBleHByZXNzaW9uIGRhdGEgaXMgZG9uZSBieSBpdGVyYXRpdmVseSBjYWxsIFtTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKSdzIGBSZWFkMTBYKClgIGFuZCBgQ3JlYXRlU2V1cmF0T2JqZWN0KClgIGZ1bmN0aW9uIG92ZXIgYWxsIEdTTXMuIFRoZXkgY29vcGVyYXRpdmVseSBmaXJzdCByZWFkIGEgMTBYIGdlbm9taWNzIHN0cnVjdHVyZWQgZmlsZSBhbmQgY3JlYXRlIGEgYFNldXJhdCBPYmplY3RgLCB3aGljaCBpcyB0aGUgYmFzaWMgdW5pdCBmb3IgYW55IGRhdGEgbWFuaXB1bGF0aW9uIGluIFtTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKS4KCmBgYHtyfQojIEdldCB0aGUgbGlzdCBvZiBzYW1wbGUgc3ViZGlyZWN0b3JpZXMKc2FtcGxlX2RpcnMgPC0gbGlzdC5kaXJzKGRlc3RfZGlyLCByZWN1cnNpdmUgPSBGQUxTRSkKCiMgRGVmaW5lIGEgbGlzdCBzdG9yaW5nIHNldXJhdCBvYmogb2YgYWxsIEdTTXMKc2V1cmF0X2xpc3QgPC0gbGlzdCgpCgptZXNzYWdlKHBhc3RlKCLwn5qAIExvYWRpbmciLCBsZW5ndGgoc2FtcGxlX2RpcnMpLCAic2FtcGxlcy4uLiIpKQpmb3IgKGRpciBpbiBzYW1wbGVfZGlycykgewogICMgRXh0cmFjdCBHU00gSUQgZnJvbSB0aGUgZm9sZGVyIG5hbWUgKGUuZy4sICJHU002Mzg1NDM4IikKICBnc21faWQgPC0gYmFzZW5hbWUoZGlyKQogIAogICMgQS4gUmVhZCAxMHggRGF0YQogICMgQmVjYXVzZSB5b3UgcmVuYW1lZCB0aGUgZmlsZXMsIFJlYWQxMFggZmluZHMgdGhlbSBhdXRvbWF0aWNhbGx5IQogIGNvdW50cyA8LSBSZWFkMTBYKGRhdGEuZGlyID0gZGlyKQogIAogICMgQi4gQ3JlYXRlIFNldXJhdCBPYmplY3QKICBzZXVyYXRfbGlzdFtbZ3NtX2lkXV0gPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KAogICAgY291bnRzID0gY291bnRzLCAKICAgIHByb2plY3QgPSBnc21faWQsIAogICAgbWluLmNlbGxzID0gMCwgCiAgICBtaW4uZmVhdHVyZXMgPSAwCiAgKQogIAogIG1lc3NhZ2UocGFzdGUoIiAg4pyFIExvYWRlZDoiLCBnc21faWQpKQp9Cmxlbmd0aChzZXVyYXRfbGlzdCkKYGBgCgpIb3dldmVyLCB0aGUgbWV0YWRhdGEgKGNvbnRhaW4gY29uZGl0aW9uIGluZm9ybWF0aW9uKSBuZWVkIHRvIGZldGNoZWQgZnJvbSBbR0VPbWV0YWRiXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0dFT21ldGFkYi5odG1sKSwgYW5kIGV4dHJhY3QgZnJvbSBgR1NNYCB0YWJsZSdzIGBjaGFyYWN0ZXJpc3RpY3NfY2gxYCBjb2x1bW4sIGFuZCB0aGVuIGluc2VydCBpbnRvIGNvcnJlc3BvbmRpbmcgU2V1cmF0IG9iamVjdCdzIGBtZXRhLmRhdGFgIGxheWVyLgoKYGBge3J9CiMgTG9hZCBHU01zIG1ldGFkYXRhIGZyb20gR0VPbWV0YWRiCmdzbV9saXN0X3N0cmluZyA8LSBwYXN0ZShwYXN0ZTAoIiciLCBzbl9nc21fbGlzdCwgIiciKSwgY29sbGFwc2UgPSAiLCIpCnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBnc20sIHRpdGxlLCBjaGFyYWN0ZXJpc3RpY3NfY2gxICIsCiAgIkZST00gZ3NtICIsCiAgIldIRVJFIGdzbSBJTiAoIiwgZ3NtX2xpc3Rfc3RyaW5nLCAiKSIKKQptZXRhZGF0YSA8LSBkYkdldFF1ZXJ5KGNvbiwgc3FsKQoKIyBNZXJnZSB0aGUgY29uZGl0aW9uIG1ldGFkYXRhIHRvIGNvcnJlc3BvbmRpbmcgU2V1cmF0IG9iaidzIG1ldGFkYXRhCm1ldGFkYXRhJENvbmRpdGlvbiA8LSBzdWIoIi4qY29uZGl0aW9uOlxccyooW147XSspLioiLCAiXFwxIiwgbWV0YWRhdGEkY2hhcmFjdGVyaXN0aWNzX2NoMSwgaWdub3JlLmNhc2UgPSBUUlVFKQoKIyBMb29wIGFuZCBhc3NpZ24KZm9yIChnc20gaW4gbmFtZXMoc2V1cmF0X2xpc3QpKSB7CiAgIyBGaW5kIHRoZSBjb25kaXRpb24gZm9yIHRoaXMgR1NNCiAgdmFsIDwtIG1ldGFkYXRhJENvbmRpdGlvblttZXRhZGF0YSRnc20gPT0gZ3NtXQogIAogICMgQXNzaWduIHRvIFNldXJhdCBvYmplY3QgKGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzKQogIGlmIChsZW5ndGgodmFsKSA+IDApIHsKICAgIHNldXJhdF9saXN0W1tnc21dXSRDb25kaXRpb24gPC0gdmFsCiAgfSBlbHNlIHsKICAgIHNldXJhdF9saXN0W1tnc21dXSRDb25kaXRpb24gPC0gIlVua25vd24iCiAgfQp9CgojIEZpbmFsIE1lcmdlIGFsbCBzYW1wbGVzIGludG8gb25lIHNldXJhdCBvYmoKY29tYmluZWRfc2V1cmF0IDwtIG1lcmdlKAogeCA9IHNldXJhdF9saXN0W1sxXV0sIAogeSA9IHNldXJhdF9saXN0Wy0xXSwgCiBhZGQuY2VsbC5pZHMgPSBuYW1lcyhzZXVyYXRfbGlzdCksIAogcHJvamVjdCA9ICJHU0UyMDk1NTIiCikKIyBWZXJpZnkKdGFibGUoY29tYmluZWRfc2V1cmF0JENvbmRpdGlvbikKYGBgCgojIyBGcmVlIG1lbW9yeQoKYGBge3J9CiMgRGlzY29ubmVjdCBTUUxpdGUgY29ubmVjdGlvbgpkYkRpc2Nvbm5lY3QoY29uKQoKIyBEZWxldGUgbWVtb3J5IHBvaW50ZXIgZnJvbSBSCnJtKGNvbikKcm0oc2V1cmF0X2xpc3QpCnJtKGNvdW50cykKcm0obWV0YWRhdGEpCgojICMgRGUgYWxsb2NhdGUgdW51c2VkIG1lbW9yeQpnYygpCmBgYAoKIyMgRGF0YSBleHBsb3JpYXRpb24geyNkYXRhLWV4cGxvcmlhdGlvbn0KClBsb3QgdGhlIGZvbGxvd2luZyBzdGF0aXN0aWNzOgoKLSAgIE51bWJlciBvZiB1bmlxdWUgaWRlbnRpZmllciAoaW5mZXIgY292ZXJhZ2UpCi0gICBOdW1iZXIgb2YgdG90YWwgYW1vdW50IG9mIGlkZW50aWZpZXIgKGluZmVyIGRlcHRoKQotICAgTnVtYmVyIG9mIHNhbXBsZXMKLSAgIE51bWJlciBvZiBjZWxscwoKYGBge3IgcWMtcGxvdC1wZXItY29uZGl0aW9uLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTQsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhdGNod29yaykKCnRoZW1lX3NldCh0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDI2KSkKCiMgMS4gQ292ZXJhZ2UgKG5GZWF0dXJlX1JOQSkKcF9jb3YgPC0gVmxuUGxvdCgKICBjb21iaW5lZF9zZXVyYXQsIAogIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIAogIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIsIAogIHB0LnNpemUgPSAwCikgKyAKICBnZ3RpdGxlKCJDb3ZlcmFnZSIpICsgCiAgeWxhYigibkZlYXR1cmVfUk5BIikKCiMgMi4gRGVwdGggKG5Db3VudF9STkEpIHdpdGggYWRqdXN0ZWQgYXhpcwpwX2RlcHRoIDwtIFZsblBsb3QoCiAgY29tYmluZWRfc2V1cmF0LCAKICBmZWF0dXJlcyA9ICJuQ291bnRfUk5BIiwgCiAgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIiwgCiAgcHQuc2l6ZSA9IDAKKSArIAogIGdndGl0bGUoIkRlcHRoIikgKyAKICB5bGFiKCJuQ291bnRfUk5BIikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDUwMDAwKSkgCgojIENvbWJpbmUgdGhlbSB0byByZWNyZWF0ZSBwX21ldHJpY3MKcF9tZXRyaWNzIDwtIHBfY292IHwgcF9kZXB0aAoKIyBOdW1iZXIgb2YgY2VsbHMgYW1vbmcgY29uZGl0aW9ucwptZXRhX2RhdGEgPC0gY29tYmluZWRfc2V1cmF0QG1ldGEuZGF0YQoKIyBBLiBCYXIgcGxvdDogVG90YWwgTnVtYmVyIG9mIENlbGxzIHBlciBDb25kaXRpb24KcF9jZWxscyA8LSBnZ3Bsb3QobWV0YV9kYXRhLCBhZXMoeCA9IENvbmRpdGlvbiwgZmlsbCA9IENvbmRpdGlvbikpICsKICBnZW9tX2Jhcihjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIENlbGxzIHBlciBHcm91cCIsIHkgPSAiTnVtYmVyIG9mIENlbGxzIiwgeCA9IE5VTEwpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIE51bWJlciBvZiBzYW1wbGVzIGFtb25nIGNvbmRpdGlvbnMKcF9zYW1wbGVzIDwtIG1ldGFfZGF0YSAlPiUKICBncm91cF9ieShDb25kaXRpb24pICU+JQogIHN1bW1hcml6ZShTYW1wbGVfQ291bnQgPSBuX2Rpc3RpbmN0KG9yaWcuaWRlbnQpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBDb25kaXRpb24sIHkgPSBTYW1wbGVfQ291bnQsIGZpbGwgPSBDb25kaXRpb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBTYW1wbGVfQ291bnQpLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnModGl0bGUgPSAiVG90YWwgU2FtcGxlcyAoR1NNcykgcGVyIEdyb3VwIiwgeSA9ICJOdW1iZXIgb2YgU2FtcGxlcyAoR1NNcykiLCB4ID0gTlVMTCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgTWVyZ2UgcGxvdHMKZmluYWxfcGxvdCA8LSBwX21ldHJpY3MgLyAocF9zYW1wbGVzIHwgcF9jZWxscykKCiMgQWRqdXN0IGZvbnQgc2l6ZQpmaW5hbF9wbG90IDwtIGZpbmFsX3Bsb3QgJiB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KQpmaW5hbF9wbG90CmBgYAoKKipGaWd1cmUgMjoqKiBPdmVyYWxsIHN0YXRpc3RpY3Mgb2YgZGF0YXNldCBgR1NFMjA5NTUyYCBncm91cGVkIGJ5IGNvbmRpdGlvbiAoQ29udHJvbCB2LnMuIHRiaSkuIFRoZSB1cHBlci1sZWZ0IHBhbmVsIGluZGljYXRlIHRoZSBudW1iZXIgb2YgdW5pcXVlIGlkZW50aWZpZXI7IFRoZSB1cHBlci1yaWdodCBwYW5lbCBpbmRpY2F0ZSB0aGUgdG90YWwgYW1vdW50IG9mIGlkZW50aWZpZXIsIHRoZSBtYXhpbXVtIGFtb3VudCBvZiBpZGVudGlmaWVyIHdhcyBsaW1pdGVkIHVwIHRvIDUwMDAwIGZvciB0aGUgc2FrZSBvZiBhZXN0aGV0aWNzOyBUaGUgbG93ZXItbGVmdCBwYW5lbCBpbmRpY2F0ZSBudW1iZXIgb2Ygc2FtcGxlcyAoR1NNcyk7IFRoZSBsb3dlci1yaWdodCBwYW5lbCBpbmRpY2F0ZSB0aGUgbnVtYmVyIG9mIGNlbGxzIjsgWC1heGlzIG9mIGFsbCBmb3VyIHBhbmVsIGluZGljYXRlIGNvbmRpdGlvbi4KCkdlbmVyYWxseSwgY29udHJvbCBncm91cCBoYXZlIGhpZ2hlciBkZXB0aCBhbmQgY292ZXJhZ2UsIGJ1dCBsZXNzIHNhbXBsZSBhbmQgY2VsbHMuCgpUaGVuIHBsb3QgdGhlIHN0YXRpc3RpY3MgYmVsb3cgcGVyIHNhbXBsZSAoR1NNKToKCi0gICBOdW1iZXIgb2YgdW5pcXVlIGlkZW50aWZpZXIgKGluZmVyIGNvdmVyYWdlKQotICAgTnVtYmVyIG9mIHRvdGFsIGFtb3VudCBvZiBpZGVudGlmaWVyIChpbmZlciBkZXB0aCkKLSAgIE51bWJlciBvZiBjZWxscwoKYGBge3IgcWMtcGxvdC1wZXItc2FtcGxlLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTQsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KCiMgRGVmaW5lIHRoZSB2ZXJ0aWNhbCBsaW5lIHN0eWxlIG9uY2UgdG8ga2VlcCBpdCBjb25zaXN0ZW50CnZlcnRpY2FsX2xpbmUgPC0gZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNS41LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMSkKCiMgMS4gQ292ZXJhZ2UgKG5GZWF0dXJlX1JOQSkKcF9jb3YgPC0gVmxuUGxvdCgKICBjb21iaW5lZF9zZXVyYXQsIAogIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIAogIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCAKICBwdC5zaXplID0gMAopICsgCiAgZ2d0aXRsZSgiQ292ZXJhZ2UiKSArIAogIHlsYWIoIm5GZWF0dXJlX1JOQSIpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkgIyBPZnRlbiBjbGVhbmVyIHRvIHJlbW92ZSB4LWxhYmVsIGlmIHRleHQgaXMgcm90YXRlZAogICkgKyB2ZXJ0aWNhbF9saW5lCgojIDIuIERlcHRoIChuQ291bnRfUk5BKSB3aXRoIGFkanVzdGVkIGF4aXMKcF9kZXB0aCA8LSBWbG5QbG90KAogIGNvbWJpbmVkX3NldXJhdCwgCiAgZmVhdHVyZXMgPSAibkNvdW50X1JOQSIsIAogIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCAKICBwdC5zaXplID0gMAopICsgCiAgZ2d0aXRsZSgiRGVwdGgiKSArIAogIHlsYWIoIm5Db3VudF9STkEiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNTAwMDApKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpCiAgKSArIHZlcnRpY2FsX2xpbmUKCiMgQS4gQmFyIHBsb3Q6IFRvdGFsIE51bWJlciBvZiBDZWxscyBwZXIgU2FtcGxlCm1ldGFfZGF0YSA8LSBjb21iaW5lZF9zZXVyYXRAbWV0YS5kYXRhICMgRW5zdXJlIG1ldGFfZGF0YSBpcyBkZWZpbmVkCnBfY2VsbHMgPC0gZ2dwbG90KG1ldGFfZGF0YSwgYWVzKHggPSBvcmlnLmlkZW50LCBmaWxsID0gb3JpZy5pZGVudCkpICsKICBnZW9tX2Jhcihjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIENlbGxzIHBlciBTYW1wbGUiLCB5ID0gIk51bWJlciBvZiBDZWxscyIsIHggPSBOVUxMKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiCiAgKSArCiAgY29vcmRfZml4ZWQocmF0aW8gPSAwLjAwMDUpICArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNjAwMCkpICsgdmVydGljYWxfbGluZQoKIyBNZXJnZSBwbG90cwpmaW5hbF9wbG90IDwtIHBfY292IC8gcF9kZXB0aCAvIHBfY2VsbHMgKyAKICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAxLCAxKSkKCiMgQWRqdXN0IGZvbnQgc2l6ZSAoYW5kIHJlLWFwcGx5IHRoZW1lIGVsZW1lbnRzIGlmIG5lZWRlZCBieSB0aGUgYmFzZSB0aGVtZSkKZmluYWxfcGxvdCA8LSBmaW5hbF9wbG90ICYgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgJiAKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICApCgpmaW5hbF9wbG90CmBgYAoKKipGaWd1cmUgMzoqKiBPdmVyYWxsIHN0YXRpc3RpY3MgcGVyIHNhbXBsZSAoR1NNKS4gVGhlIHRvcCBwYW5lbCBpbmRpY2F0ZSBudW1iZXIgb2YgdW5pcXVlIGlkZW50aWZpZXI7IFRoZSBtaWRkbGUgcGFuZWwgaW5kaWNhdGUgbnVtYmVyIG9mIHRvdGFsIGlkZW50aWZpZXI7IFRoZSBib3R0b20gcGFuZWwgaW5kaWNhdGUgbnVtYmVyIG9mIGNlbGxzOyBYLWF4aXMgb2YgYWxsIHRocmVlIHBhbmVsIGluZGljYXRlIHNhbXBsZXMgKEdTTXMpOyBUaGUgdmVydGljYWwgcmVkIGRhc2ggbGluZSBzZXBhcmF0ZSBjb250cm9sIChsZWZ0IHNpZGUgb2YgcmVkIGxpbmUpLCBhbmQgdGJpIChyaWdodCBzaWRlIG9mIHJlZCBsaW5lKS4KCkluIGdlbmVyYWwsIHRoZSBzZXF1ZW5jZSBkZXB0aCBhbmQgY292ZXJhZ2UgaXMgbW9yZSBjb25zaXN0ZW5jZSBhbW9uZyBjb250cm9sIGdyb3VwLiBUYmkgZ3JvdXAgaGFzIG1vcmUgdmFyaWF0ZWQgZGVwdGggYW5kIGNvdmVyYWdlIHdpdGggYEdTTTYzNzY4MWAgYmVpbmcgdGhlIHNhbXBsZSBvZiBsb3dlc3QgZGVwdGhzIGFuZCBjb3ZlcmFnZS4gVGhlIG51bWJlciBvZiBjZWxscyB3aXRoaW4gZGlmZmVyZW50IHNhbXBsZSBhbHNvIHZhcmllcywgZnJvbSBoaWdoZXN0IDM3MDggKGBHU002Mzc2ODIyYCkgdG8gbG93ZXN0IDM1OCAoYEdTTTYzNzY4MjBgKS4gVGhpcyBpbWJhbGFuY2UgaW4gY2VsbCBudW1iZXIgbWF5IHJlc3VsdCBpbiBkb3duc3RyZWFtIGFuYWx5c2lzIGRvbWluYXRlIGJ5IHNhbXBsZSB3aGljaCBjb250YWluIG1vcmUgY2VsbHMuCgpBbm90aGVyIGNvbW1vbiBxdWFsaXR5IGNvbnRyb2wgc3RlcCBpbiBgc2NSTkEtc2VxYCBpcyB0byByZW1vdmUgYGRhbWFnZWQgY2VsbHNgLiBXaGVuIGRhbWFnZWQgY2VsbHMgd2VyZSBzZXF1ZW5jZWQgb3IgZGFtYWdlZCBkdXJpbmcgc2VxdWVuY2luZywgdGhlIGV4cHJlc3Npb24gaW50ZW5zaXR5IHdpbGwgc2NhbGVkIGRvd24gZHVlIHRvIGxlYWtlZCBSTkEsIHRodXMgc2lnbmlmaWNhbnRseSBiaWFzIGRvd25zdHJlYW0gY2x1c3RlcmluZywgYW5ub3RhdGlvbiwgYW5kIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMuIEEgY29tbW9uIG1ldGhvZCB0byBjb25kdWN0IHRoaXMgZmlsdGVyaW5nIGlzIHVzaW5nIGBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZWAsIGJhc2VkIG9uIHRoaXMgaWRlYSB0aGF0IGlmIGEgY2VsbCBoYXMgbGVha2VkIFJOQSwgdGhlIGBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZWAgd2lsbCBiZSByZWxhdGl2ZWx5IGhpZ2hlciBzaW5jZSBtaXRvY2hvbmRyaWEgaXMgcmVsYXRpdmVseSBsYXJnZSBhbmQgbGVzcyBsaWtlbHkgdG8gbGVhay4gRW1waXJpY2FsbHksIHRoZSB0aHJlc2hvbGQgaXMgc2V0IHRvIGJlIGA1JSB+IDEwJWAKCkhvd2V2ZXIsIHdoZW4gaXQgY29tZXMgdG8gYHNuUk5BLXNlcWAsIGBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZWAgaXMgZXhwZWN0ZWQgdG8gYmUgYDAlYCBiZWNhdXNlIG9mIHRoZSBleGNsdXNpb24gb2YgY3l0b3BsYXNtLCBhbmQgYW55IG5vbmUtemVybyBgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2VgIGluZGljYXRlIGluY29tcGxldGUgZXhjbHVzaW9uIG9mIGN5dG9wbGFzbSBbQW1lenF1aXRhIGV0IGFsLCAyMDIwXShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE1OTItMDE5LTA2NTQteCkuIFRodXMgSSBtYXkgd2FudCB0byBjaG9pY2UgYSBtb3JlIHN0cmljdCB0aHJlc2hvbGQgZm9yIGBzblJOQS1zZXFgLCBsaWtlIGAxJSB+IDIlYCwgdG8gbWF0Y2ggdGhlIHdldC1sYWIgZGVzaWduIHdoaWxlIHRvbGVyYXRlIGxvdy1xdWFsaXR5IGNlbGwgdG8gY2VydGFpbiBleHRlbnQuCgpgYGB7ciBtdC1wbG90LCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTQsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KIyBVc2Ugc2V1cmF0IFBlcmNlbnRhZ2VGZWF0dXJlU2V0KCkgZnVuY3Rpb24KIyBDcmVhdGUgYSBuZXcgZW50cnkgaW4gbWV0YS5kYXRhIGxheWVyIHN0b3JpbmcgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2UKY29tYmluZWRfc2V1cmF0W1sicGVyY2VudC5tdCJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChjb21iaW5lZF9zZXVyYXQsIHBhdHRlcm4gPSAiXk1ULSIpCgojIERlZmluIGEgaG9yaXpvbnRhbCBsaW5lIGF0IFkgPT0gNSBpbmRpY2F0ZSBlbXBpcmljYWwgbWl0byUgdGhyZWFzaG9sZApob3Jpem9udGFsX2xpbmUgPC0gZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMTAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIsIHNpemUgPSAxKQoKIyBQZXJjZW50IE1UIHBlciBDT05ESVRJT04gKENvbnRyb2wgdnMgVEJJKQpwX2NvbmRpdGlvbiA8LSBWbG5QbG90KGNvbWJpbmVkX3NldXJhdCwgZmVhdHVyZXMgPSBjKCJwZXJjZW50Lm10IiksIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIsIHB0LnNpemUgPSAwKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiICMgSGlkZSBsZWdlbmQgc2luY2UgeC1heGlzIGFscmVhZHkgc2hvd3MgdGhlIG5hbWVzCiAgKSArIAogIGdndGl0bGUoIk1pdG9jaG9uZHJpYWwgJSBieSBDb25kaXRpb24iKSArIAogIGhvcml6b250YWxfbGluZSArIAogIHlsYWIoIk1pdG9jaG9uZHJpYWwgVHJhbnNjcmlwdCAoJSkiKSArIAogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDAuNSwgeSA9IDEyLCBsYWJlbCA9ICJtaXRvICUgPSAxMCIsIGNvbG9yID0gImJsYWNrIiwgaGp1c3QgPSAwLCBzaXplID0gNSkKCiMgUGVyY2VudCBNVCBwZXIgR1NNCnBfc2FtcGxlIDwtIFZsblBsb3QoY29tYmluZWRfc2V1cmF0LCBmZWF0dXJlcyA9IGMoInBlcmNlbnQubXQiKSwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsIHB0LnNpemUgPSAwKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgIyBSb3RhdGUgeC1heGlzIGxhYmVscyA0NSBkZWdyZWVzIHNvIHRoZXkgZG9uJ3Qgb3ZlcmxhcAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiAjIEhpZGUgbGVnZW5kIHNpbmNlIHgtYXhpcyBhbHJlYWR5IHNob3dzIHRoZSBuYW1lcwogICkgKyB2ZXJ0aWNhbF9saW5lICsgCiAgZ2d0aXRsZSgiTWl0b2Nob25kcmlhbCAlIGJ5IFNhbXBsZSIpICsgCiAgaG9yaXpvbnRhbF9saW5lICsgCiAgeWxhYigiTWl0b2Nob25kcmlhbCBUcmFuc2NyaXB0ICglKSIpICsgCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMC41LCB5ID0gMTIsIGxhYmVsID0gIm1pdG8gJSA9IDEwIiwgY29sb3IgPSAiYmxhY2siLCBoanVzdCA9IDAsIHNpemUgPSA1KQoKIyA0LiBDb21iaW5lIHRoZW0KZmluYWxfbXRfcGxvdCA8LSBwX2NvbmRpdGlvbiAvIHBfc2FtcGxlCmZpbmFsX210X3Bsb3QKYGBgCgoqKkZpZ3VyZSA0OioqIE1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlIGdyb3VwZWQgYnkgR1NNcy4gVGhlIFktYXhpcyBpbmRpY2F0ZSBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZTsgVGhlIFgtYXhpcyBvZiB0b3AgcGFuZWwgaW5kaWNhdGUgY29uZGl0aW9uIChDb250cm9sIHYucy4pOyBUaGUgWCBheGlzIG9mIGJvdHRvbSBwYW5lbCBpbmRpY2F0ZSBHU00gc2FtcGxlczsgVGhlIGhvcml6b250YWwgcmVkIGRhc2ggbGluZSBpbmRpY2F0ZSBlbXBpcmljYWwgTWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2UgdGhyZXNob2xkID0gMTAlOyBUaGUgdmVydGljYWwgcmVkIGRhc2ggbGluZSBhdCBib3R0b20gcGFuZWwgc2VwYXJhdGUgY29udHJvbCAobGVmdCBzaWRlIG9mIHJlZCBsaW5lKSwgYW5kIHRyZWF0bWVudCAocmlnaHQgc2lkZSBvZiByZWQgbGluZSkuCgpIb3dldmVyLCB0aGUgYE1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlYCBpcyB3YXkgaGlnaGVyIHRoYW4gZXhwZWN0ZWQgaW4gYWxsIHNhbXBsZXMgYW5kIHRoZSBvcmlnaW5hbCBwYXBlciBkaWRuJ3QgYXNzZXNzIHRoZSBgTWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2VgIGF0IGFsbCAoW0dhcnphIGV0IGFsLCAyMDIzXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNlbHJlcC4yMDIzLjExMzM5NSkpLiBPbmUgY2x1ZSB0aGF0IG1heSBleHBsYWluIHRoaXMgdW5leHBlY3RlZCBoaWdoIGBNaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZWAgZnJvbSB0aGVpciBgTGltaXRhdGlvbnMgb2YgdGhlIHN0dWR5YCBpcyBjb250cm9sIHRpc3N1ZXMgd2VyZSBwb3N0LW1vcnRlbSBzYW1wbGUgYW5kIHRiaSB0aXNzdWVzIHdlcmUgc3VyZ2ljYWxseSBldmFjdWF0ZWQgcmFyZSBjYXNlIHBvc3QtaW1wYWN0LiBUaHVzIHRoZSBpbnRlZ3JpdHkgb2Ygc2FtcGxlcyB3ZXJlIG5vdCBhdCBpZGVhbCBjb25kaXRpb24gYW5kIGV4cGVjdGVkIHRvIGNvbnRhaW4gZGFtYWdlZCBjZWxscy4KCk9uIHRoZSBvdGhlciBoYW5kcywgYG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlYCBhbGlnbmVkIHdpdGggcHJldmlvdXMgZGVwdGggYW5kIGNvdmVyYWdlIGFtb25nIHNhbXBsZXMsIHdoZXJlIGNvbnRyb2wgZ3JvdXAgdGVuZCB0byBiZSBtb3JlIGNvbnNpc3RlbnQuIEEgaHlwb3RoZXNpcyBpcyB0aGF0IFRyYXVtYXRpYyBCcmFpbiBJbmp1cnkgKFRCSSkgaXMgYSBicm9hZCB0ZXJtIHRoYXQgaW5jbHVkZSB2YXJpb3VzIGJyYWluIGRhbWFnZSByZXN1bHQgZnJvbSBleHRlcm5hbCBmb3JjZSwgYW5kIHRoZSBpbnRlbnNpdHkgYW5kIGV4YWN0IHBhdGhvbG9neSBtYXkgdmFyeSBhbW9uZyBkaWZmZXJlbnQgdGJpIHNhbXBsZSwgcmVmbGVjdGVkIGJ5IHNlcXVlbmNlIGRlcHRoIGFuZCBjb3ZlcmFnZS4gTm90aWNlYWJseSwgYEdTTTYzNzY4MTRgIGhhcyByZWxhdGl2ZWx5IGhpZ2hlciBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZSwgYW5kIHJlY2FsbCB0aGF0IGl0J3MgYWxzbyB0aGUgc2FtcGxlIGhhdmluZyBsb3dlc3QgZGVwdGggYW5kIGNvdmVyYWdlLCB3aGljaCBtaWdodCBpbXBseSBleGlzdGluZyB0ZWNobmljYWwgdmFyaWFuY2UgYW5kL29yIGJhdGNoIGVmZmVjdCB0aGF0IGNvdWxkIGJpYXMgZG93bnN0cmVhbSBhbmFseXNpcy4KCiMjIE1hcHBpbmcgdG8gSFVHTyBzeW1ib2xzIHsjbWFwcGluZy10by1odWdvLXN5bWJvbHN9CgpDaGVjayB0aGUgY3VycmVudCBpZGVudGlmaWVyCgpgYGB7cn0KaGVhZChyb3duYW1lcyhjb21iaW5lZF9zZXVyYXQpKQoKbGVuZ3RoKHJvd25hbWVzKGNvbWJpbmVkX3NldXJhdCkpID09IGxlbmd0aCh1bmlxdWUocm93bmFtZXMoY29tYmluZWRfc2V1cmF0KSkpCmBgYAoKQXQgYSBnbGFuY2UsIGl0J3MgYEhVR09gIHN5bWJvbC4gVG8gdmVyaWZ5LCBJIGNhbiBxdWVyeSB0aG9zZSBzeW1ib2wgYWdhaW5zdCBgb3JnLkhzLmVnLmRiYC4gQW5kIGBUUlVFYCBpbmRpY2F0ZSB0aGUgbnVtYmVyIG9mIGdlbmUgc3ltYm9sIGlzIGVxdWFsIHRvIHRoZSBudW1iZXIgb2YgdW5pcXVlIGdlbmUgc3ltYm9sLCB0aHVzIG5vIGR1cGxpY2F0ZS4KCmBgYHtyfQojIEluc3RhbGwgb3JnLkhzLmVnLmRiCmlmICghcmVxdWlyZU5hbWVzcGFjZSgib3JnLkhzLmVnLmRiIiwgcXVpZXRseSA9IFRSVUUpKSB7CiAgQmlvY01hbmFnZXI6Omluc3RhbGwoIm9yZy5Icy5lZy5kYiIpCn0KCiMgQXR0YWNoIG9yZy5Icy5lZy5kYiB0byBjdXJyZW50IHNlc3Npb24KbGlicmFyeShvcmcuSHMuZWcuZGIpCgojIENoZWNrIGhvdyBtYW55IG9mIHRoZW0gZXhpc3QgaW4gdGhlIG9mZmljaWFsIHN5bWJvbCBkYXRhYmFzZQp2YWxpZF9zeW1ib2xzIDwtIG1hcElkcyhvcmcuSHMuZWcuZGIsIAogICAgICAgICAgICAgICAgICAgICAgICBrZXlzID0gcm93bmFtZXMoY29tYmluZWRfc2V1cmF0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbiA9ICJTWU1CT0wiLCAKICAgICAgICAgICAgICAgICAgICAgICAga2V5dHlwZSA9ICJTWU1CT0wiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbXVsdGlWYWxzID0gImZpcnN0IikKCiMgQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIG1hdGNoZXMKc3VtKCFpcy5uYSh2YWxpZF9zeW1ib2xzKSkgLyBsZW5ndGgocm93bmFtZXMoY29tYmluZWRfc2V1cmF0KSkgKiAxMDAKYGBgCgpgMTAwJWAgdmFsaWQgc3ltYm9scyBjb25maXJtZWQgdGhlIHN5bWJvbCBpcyBhY3R1YWxseSBgSFVHT2Agc3ltYm9sLgoKIyMgQ2xlYW5pbmcKCkZpcnN0IEkgcmVtb3ZlIGBHU002Mzc2ODE0YCwgc2luY2UgaXQgaGFzIHRoZSBsb3dlc3QgZGVwdGgsIGNvdmVyYWdlLCBhbmQgaGlnaGVzdCBgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2VgLCB0aHVzIHRlbmQgdG8gY29udGFtaW5hdGUgZG93bnN0cmVhbSBhbmFseXNpcy4KCmBgYHtyfQpjb21iaW5lZF9zZXVyYXQgPC0gc3Vic2V0KGNvbWJpbmVkX3NldXJhdCwgc3Vic2V0ID0gb3JpZy5pZGVudCAhPSAiR1NNNjM3NjgxNCIpCmBgYAoKQmFzZWQgb24gW1NldXJhdF0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC8pIGFuZCBbSGV1bW9zIGV0IGFsLCAyMDIzXShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE1NzYtMDIzLTAwNTg2LXcpLCB0aGV5IHNpbXBseSBhcHBsaWVkIGhhcmQgdGhyZXNob2xkIG9uIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlLCBgNSVgIGFuZCBgOCVgLCBhdCB0aGVpciBxdWFsaXR5IGNvbnRyb2wgc3RlcC4gQWRkaXRpb25hbGx5LCByZWZlciB0byBwcmV2aW91cyBgRmlndXJlIDNgLCBJIGVtcGlyaWNhbGx5IGNob2ljZSB0aGUgdGhyZXNob2xkIHRvIGJlIGAxMCVgLgoKYGBge3J9CmNvbWJpbmVkX3NldXJhdCA8LSBzdWJzZXQoY29tYmluZWRfc2V1cmF0LCBzdWJzZXQgPSBwZXJjZW50Lm10IDw9IDEwKQpgYGAKCkluIHRoZSBlbmQsIGAyODg3LzI0NjIxID0gMTEuNyVgIGNlbGxzIHdlcmUgcmVtb3ZlZC4gQ29tcGFyZSB3aXRoIG9yaWdpbmFsIHBhcGVyLCB0aGV5IHJlbW92ZWQgYDk1MTEvMjQ2MjEgPSAzOC42JWAgY2VsbHMgKFtHYXJ6YSBldCBhbCwgMjAyM10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jZWxyZXAuMjAyMy4xMTMzOTUpKS4gSSBzdXBwb3NlIHBhcnQgb2YgdGhlIHJlYXNvbiB0aGV5IGFwcGx5IHN1Y2ggcmVzdHJpY3QgcXVhbGl0eSBjb250cm9sIGlzIHRoZXkgYXJlIGF3YXJlIG9mIHRoZSBsb3cgaW50ZWdyaXR5IG9mIHNhbXBsZXMuCgpgYGB7ciBmaWx0ZXJlZC1wbG90LCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTAsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KIyAxLiBNaXRvICUgUGxvdCAoQWZ0ZXIgQ2xlYW5pbmcpCnBfbWl0b19jbGVhbiA8LSBWbG5QbG90KAogIGNvbWJpbmVkX3NldXJhdCwgCiAgZmVhdHVyZXMgPSAicGVyY2VudC5tdCIsIAogIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCAKICBwdC5zaXplID0gMAopICsgCiAgdmVydGljYWxfbGluZSArIAogIGdndGl0bGUoIk1pdG9jaG9uZHJpYWwgJSBwZXIgU2FtcGxlIChBZnRlciBDbGVhbmluZykiKSArIAogIHlsYWIoIk1pdG9jaG9uZHJpYWwgVHJhbnNjcmlwdCAoJSkiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDEwKSkgKyAjIFpvb20gaW4gc2luY2UgbWF4IGlzIG5vdyA8PSAxMAogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkKICApICsKICBjb29yZF9maXhlZChyYXRpbyA9IDAuNSkKCiMgMi4gVG90YWwgQ2VsbHMgUGxvdCAoWW91ciBleGlzdGluZyBjb2RlKQptZXRhX2RhdGEgPC0gY29tYmluZWRfc2V1cmF0QG1ldGEuZGF0YSAKcF9jZWxscyA8LSBnZ3Bsb3QobWV0YV9kYXRhLCBhZXMoeCA9IG9yaWcuaWRlbnQsIGZpbGwgPSBvcmlnLmlkZW50KSkgKwogIGdlb21fYmFyKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnModGl0bGUgPSAiVG90YWwgQ2VsbHMgcGVyIFNhbXBsZSAoQWZ0ZXIgQ2xlYW5pbmcpIiwgeSA9ICJOdW1iZXIgb2YgQ2VsbHMiLCB4ID0gTlVMTCkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIgogICkgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMC4wMDEpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA0MDAwKSkgKyAKICB2ZXJ0aWNhbF9saW5lCgojIDMuIENvbWJpbmUgdGhlbSB2ZXJ0aWNhbGx5CmZpbmFsX2NsZWFuX3Bsb3QgPC0gcF9taXRvX2NsZWFuIC8gcF9jZWxscwpmaW5hbF9jbGVhbl9wbG90CmBgYAoKKipGaWd1cmUgNToqKiBOdW1iZXIgb2YgY2VsbHMgYWZ0ZXIgY2xlYW5pbmcgZ3JvdXBlZCBieSBHU01zLiBUaGUgWS1heGlzIGluZGljYXRlIG51bWJlciBvZiBjZWxsczsgVGhlIFgtYXhpcyBpbmRpY2F0ZSBHU01zOyBUaGUgdmVydGljYWwgcmVkIGRhc2ggbGluZSBzZXBhcmF0ZSBjb250cm9sIChsZWZ0IHNpZGUgb2YgcmVkIGxpbmUpLCBhbmQgdGJpIChyaWdodCBzaWRlIG9mIHJlZCBsaW5lKS4KCiMgTm9ybWFsaXphdGlvbgoKRGlmZmVyIGZyb20gUk5BLXNlcSBhbmQgYnVsayBSTkEtc2VxIG5vcm1hbGl6YXRpb24gY292ZXJlZCBpbiBjbGFzcywgYHNjL3NuUk5BLXNlcWAgZGF0YSBpcyBgemVyby1pbmZsYXRlZGAsIG1vc3Qgb2YgZ2VuZSdzIGNvdW50cyBpcyBgMGAgaW4gbW9zdCBvZiBjZWxscy4gVGh1cyB0d28gb2YgdGhlIG1ldGhvZCBjb3ZlcmVkIGluIGNsYXNzLCBgVE1NYCBhbmQgYFJMRWAsIGFyZSBub3Qgc3VpdGFibGUgZm9yIGBzYy9zblJOQS1zZXFgIGJlY2F1c2UgdGhleSBib3RoIGFzc3VtZSBtb3N0IG9mIHRoZSBnZW5lIGFyZSBub3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLCBgemVyby1pbmZsYXRlZGAgY291bnRzIGRhdGEgd2lsbCB5aWVsZCBpbmZsYXRlZCBmb2xkIGNoYW5nZSB3aGVuIGV2ZXIgYW4gYWxnb3JpdGhtIGlzIHRyeWluZyB0byBjYWxjdWxhdGUgZm9sZCBjaGFuZ2UgKFtMeXRhbCBldCBhbCwgMjAyMF0oaHR0cHM6Ly9kb2kub3JnLzEwLjMzODkvZmdlbmUuMjAyMC4wMDA0MSkpLgoKIyMgTGlicmFyeSBzaXplIGJhc2VkIExvZy1Ob3JtYWxpemF0aW9uICYgZmlsdGVyIGxvdyBleHByZXNzZWQgZ2VuZQoKVGhlIHNpbXBsZSBub3JtYWxpemF0aW9uIGJ5IGxpYnJhcnkgc2l6ZSBzdGlsbCB3b3JrIGFzIGludGVuZGVkIGFuZCB3b24ndCBhZmZlY3QgYnkgaW5mbGF0ZWQgemVyb3MgYW5kIHdpZGVseSB1dGlsaXplZCBpbiBzaW5nLWNlbGwgdHJhbnNjcmlwdG9tZSBmaWVsZCBhcyBhIGJhc2VsaW5lIChbR2UgZXQgYWwsIDIwMjVdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMzcxL2pvdXJuYWwucG9uZS4wMzM1MTAyKSwgW0x5dGFsIGV0IGFsLCAyMDIwXShodHRwczovL2RvaS5vcmcvMTAuMzM4OS9mZ2VuZS4yMDIwLjAwMDQxKSwgW0FtZXpxdWl0YSBldCBhbCwgMjAxOV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvczQxNTkyLTAxOS0wNjU0LXgpKS4gSW4gYFNldXJhdGAsIGl0J3Mgd3JhcHBlZCBhcyBbTm9ybWFsaXplRGF0YSgpXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L3JlZmVyZW5jZS9ub3JtYWxpemVkYXRhKSBmdW5jdGlvbi4gTm90ZSB0aGUgZGVmYXVsdCBgc2NhbGUuZmFjdG9yYCBpcyBzZXQgdG8gYmUgYDEwMDAwYCB0byBhY2NvdW50IGZvciBgc2Mvc25STkEtc2VxYCdzIHJlbGF0aXZlIHNtYWxsIHJlYWRzIHBlciBjZWxsIGNvbXBhcmUgd2l0aCBgYnVsayBSTkEtc2VxYC4KCkZvciB2aXN1YWxpemF0aW9uLCBJIGNob2ljZSBmaXJzdCByYW5kb21seSBzYW1wbGUgb25lIGNlbGwgZnJvbSBlYWNoIGBHU01gIGFuZCBwbG90IHRoZSBwcmUgYW5kIHBvc3Qgbm9ybWFsaXplZCBjb3VudHMgb24gWS1heGlzLCBzYW1wbGVkIGNlbGwgb24gWC1heGlzLiBUaGUgcmVhc29uIG9mIG5vdCBwbG90IG5vcm1hbGl6ZWQgY291bnRzIHBlciBgR1NNYCBpcyBiZWNhdXNlIGluIGBzYy9zblJOQS1zZXFgLCB0aGUgY291bnRzIGlzIG5vcm1hbGl6ZWQgYnkgdGhlIHRvdGFsIHJlYWRzIG9mIGEgY2VsbCByYXRoZXIgdGhhbiBgR1NNYCwgdGh1cyBwbG90IG5vcm1hbGl6ZWQgY291bnRzIHBlciBgR1NNYCB3b24ndCBiZSBzbyBtZWFuaW5nZnVsLgoKSW4gYm90aCBbU2V1cmF0IHR1dG9yaWFsXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbCksIGFuZCBbSGV1bW9zIGV0IGFsLCAyMDIzXShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE1NzYtMDIzLTAwNTg2LXcpLCB0aGV5IHVzZWQgdGhlIG1pbmltdW0gbnVtYmVyIG9mIGNlbGwgZXhwcmVzc2luZyBjZXJ0YWluIGdlbmUgdG8gcmVtb3ZlIGxvdyBleHByZXNzZWQgZ2VuZSBpbnN0ZWFkIG9mIHRocmVzaG9sZGluZyBleHByZXNzaW9uIGxldmVsIGxpa2UgaW4gYGVkZ2VSYC4gVGh1cyBJIHdpbGwgYWxzbyBmaWx0ZXIgbG93IGV4cHJlc3NlZCBnZW5lIGJhc2VkIG9uIG1pbmltdW0gbnVtYmVyIG9mIGNlbGwuCgpgYGB7ciBwcmUtcG9zdC1ub3JtLXBsb3QsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD0xMCwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwLCBvdXQud2lkdGg9IjEwMCUifQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHRpYmJsZSkKCiMgQ29sbGFwc2UgdGhlIHNwbGl0IHNhbXBsZSBsYXllcnMgaW50byBhIHNpbmdsZSB1bmlmaWVkICdjb3VudHMnIGxheWVyCmNvbWJpbmVkX3NldXJhdCA8LSBKb2luTGF5ZXJzKGNvbWJpbmVkX3NldXJhdCkKCiMgQ3JlYXRlIGEgY29weSBvZiB1bi1ub3JtYWxpemVkIGRhdGEgZm9yIHRlc3QgcHVycG9zZQpzZXVyYXRfY29weSA8LSBjb21iaW5lZF9zZXVyYXQKCiMgUmFuZG9tbHkgc2FtcGxlIGV4YWN0bHkgT05FIGNlbGwgcGVyIEdTTSAob3JpZy5pZGVudCkKc2V0LnNlZWQoNDIpIApzYW1wbGVkX2NlbGxzIDwtIGNvbWJpbmVkX3NldXJhdEBtZXRhLmRhdGEgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJDZWxsX0lEIikgJT4lCiAgZ3JvdXBfYnkob3JpZy5pZGVudCkgJT4lCiAgc2FtcGxlX24oMSkgJT4lCiAgcHVsbChDZWxsX0lEKQoKIyBDcmVhdGUgYSBtaW5pLVNldXJhdCBvYmplY3Qgb2YganVzdCB0aGVzZSBjZWxscyBmb3IgZmFzdGVyIGV4dHJhY3Rpb24Kc2NfdW5maWx0ZXJlZCA8LSBzdWJzZXQoY29tYmluZWRfc2V1cmF0LCBjZWxscyA9IHNhbXBsZWRfY2VsbHMpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIFBMT1QgMTogUHJlLU5vcm1hbGl6YXRpb24gKFJhdyBDb3VudHMpCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnJhd19tYXRyaXggPC0gYXMubWF0cml4KEdldEFzc2F5RGF0YShzY191bmZpbHRlcmVkLCBsYXllciA9ICJjb3VudHMiKSkKCmRmX3JhdyA8LSBhcy5kYXRhLmZyYW1lKHJhd19tYXRyaXgpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLCBuYW1lc190byA9ICJDZWxsX0lEIiwgdmFsdWVzX3RvID0gIkNvdW50IikgJT4lCiAgbXV0YXRlKFNhbXBsZSA9IHNjX3VuZmlsdGVyZWRAbWV0YS5kYXRhW0NlbGxfSUQsICJvcmlnLmlkZW50Il0pCgpwX3ByZV9ub3JtIDwtIGdncGxvdChkZl9yYXcsIGFlcyh4ID0gU2FtcGxlLCB5ID0gQ291bnQsIGZpbGwgPSBTYW1wbGUpKSArCiAgZ2VvbV92aW9saW4oc2NhbGUgPSAid2lkdGgiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC44KSArIAogIGdndGl0bGUoIjEuIFByZS1Ob3JtYWxpemF0aW9uOiBSYXcgQ291bnRzIChVbmZpbHRlcmVkKSIpICsgCiAgeWxhYigiRXhwcmVzc2lvbiAoTGluZWFyKSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB2ZXJ0aWNhbF9saW5lICsgCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDIwMCkpCgoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBQTE9UIDI6IFBvc3QtTm9ybWFsaXphdGlvbiAoQmVmb3JlIEZpbHRlcmluZykKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBOb3JtYWxpemUgdGhlIG1haW4gZGF0YXNldCBmaXJzdApjb21iaW5lZF9zZXVyYXQgPC0gTm9ybWFsaXplRGF0YShjb21iaW5lZF9zZXVyYXQsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwKQoKIyBVcGRhdGUgbWluaS1TZXVyYXQgb2JqZWN0IHRvIGdldCB0aGUgbmV3IG5vcm1hbGl6ZWQgJ2RhdGEnIGxheWVyCnNjX3VuZmlsdGVyZWQgPC0gc3Vic2V0KGNvbWJpbmVkX3NldXJhdCwgY2VsbHMgPSBzYW1wbGVkX2NlbGxzKQoKbm9ybV9tYXRyaXhfdW5maWx0IDwtIGFzLm1hdHJpeChHZXRBc3NheURhdGEoc2NfdW5maWx0ZXJlZCwgbGF5ZXIgPSAiZGF0YSIpKQoKZGZfbm9ybV91bmZpbHQgPC0gYXMuZGF0YS5mcmFtZShub3JtX21hdHJpeF91bmZpbHQpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLCBuYW1lc190byA9ICJDZWxsX0lEIiwgdmFsdWVzX3RvID0gIk5vcm1hbGl6ZWRfQ291bnQiKSAlPiUKICBtdXRhdGUoU2FtcGxlID0gc2NfdW5maWx0ZXJlZEBtZXRhLmRhdGFbQ2VsbF9JRCwgIm9yaWcuaWRlbnQiXSkKCnBfcG9zdF91bmZpbHQgPC0gZ2dwbG90KGRmX25vcm1fdW5maWx0LCBhZXMoeCA9IFNhbXBsZSwgeSA9IE5vcm1hbGl6ZWRfQ291bnQsIGZpbGwgPSBTYW1wbGUpKSArCiAgZ2VvbV92aW9saW4oc2NhbGUgPSAid2lkdGgiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC44KSArIAogIGdndGl0bGUoIjIuIFBvc3QtTm9ybWFsaXphdGlvbjogTG9nLU5vcm1hbGl6ZWQgKFVuZmlsdGVyZWQpIikgKyAKICB5bGFiKCJFeHByZXNzaW9uIChMb2cxcCkiKSArIAogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHZlcnRpY2FsX2xpbmUgKyAKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgNikpCgoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBQTE9UIDM6IFBvc3QtTm9ybWFsaXphdGlvbiAoQWZ0ZXIgRmlsdGVyaW5nKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIENhbGN1bGF0ZSBmcmVxdWVuY2llcyBhbmQgZmlsdGVyIHRoZSBtYWluIGRhdGFzZXQKcmF3X2NvdW50c19hbGwgPC0gR2V0QXNzYXlEYXRhKGNvbWJpbmVkX3NldXJhdCwgbGF5ZXIgPSAiY291bnRzIikKY2VsbHNfcGVyX2dlbmUgPC0gcm93U3VtcyhyYXdfY291bnRzX2FsbCA+IDApCm1pbl9jZWxscyA8LSAxMApnZW5lc190b19rZWVwIDwtIG5hbWVzKHdoaWNoKGNlbGxzX3Blcl9nZW5lID49IG1pbl9jZWxscykpCgptZXNzYWdlKCJOdW1iZXIgb2YgZ2VuZXMgQkVGT1JFIGZpbHRlcmluZzogIiwgbnJvdyhyYXdfY291bnRzX2FsbCkpCm1lc3NhZ2UoIk51bWJlciBvZiBnZW5lcyBBRlRFUiBmaWx0ZXJpbmcgKD49ICIsIG1pbl9jZWxscywgIiBjZWxscyk6ICIsIGxlbmd0aChnZW5lc190b19rZWVwKSkKbWVzc2FnZSgiVG90YWwgZ2VuZXMgcmVtb3ZlZDogIiwgbnJvdyhyYXdfY291bnRzX2FsbCkgLSBsZW5ndGgoZ2VuZXNfdG9fa2VlcCkpCgojIFN1YnNldCB0aGUgbWFpbiBvYmplY3QgdG8gcmVtb3ZlIG5vaXN5LCBsb3ctZXhwcmVzc2VkIGdlbmVzCmNvbWJpbmVkX3NldXJhdF9maWx0ZXJlZCA8LSBzdWJzZXQoY29tYmluZWRfc2V1cmF0LCBmZWF0dXJlcyA9IGdlbmVzX3RvX2tlZXApCgojIENyZWF0ZSBhIG5ldyBtaW5pLVNldXJhdCBvYmplY3QgYmFzZWQgb24gdGhlIEZJTFRFUkVEIGRhdGFzZXQKc2NfZmlsdGVyZWQgPC0gc3Vic2V0KGNvbWJpbmVkX3NldXJhdF9maWx0ZXJlZCwgY2VsbHMgPSBzYW1wbGVkX2NlbGxzKQoKbm9ybV9tYXRyaXhfZmlsdCA8LSBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKHNjX2ZpbHRlcmVkLCBsYXllciA9ICJkYXRhIikpCgpkZl9ub3JtX2ZpbHQgPC0gYXMuZGF0YS5mcmFtZShub3JtX21hdHJpeF9maWx0KSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiQ2VsbF9JRCIsIHZhbHVlc190byA9ICJOb3JtYWxpemVkX0NvdW50IikgJT4lCiAgZmlsdGVyKE5vcm1hbGl6ZWRfQ291bnQgPiAwKSAlPiUKICBtdXRhdGUoU2FtcGxlID0gc2NfZmlsdGVyZWRAbWV0YS5kYXRhW0NlbGxfSUQsICJvcmlnLmlkZW50Il0pCgpwX3Bvc3RfZmlsdCA8LSBnZ3Bsb3QoZGZfbm9ybV9maWx0LCBhZXMoeCA9IFNhbXBsZSwgeSA9IE5vcm1hbGl6ZWRfQ291bnQsIGZpbGwgPSBTYW1wbGUpKSArCiAgZ2VvbV92aW9saW4oc2NhbGUgPSAid2lkdGgiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC44KSArIAogIGdndGl0bGUocGFzdGUwKCIzLiBQb3N0LU5vcm1hbGl6YXRpb246IExvZy1Ob3JtYWxpemVkIChGaWx0ZXJlZCA+PSAiLCBtaW5fY2VsbHMsICIgY2VsbHMpIikpICsgCiAgeWxhYigiRXhwcmVzc2lvbiAoTG9nMXApIikgKyAKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHZlcnRpY2FsX2xpbmUgKyAKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgNikpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIENPTUJJTkUgUExPVFMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBTdGFjayB0aGVtIHZlcnRpY2FsbHkgCmZpbmFsX25vcm1fcGxvdCA8LSBwX3ByZV9ub3JtIC8gcF9wb3N0X3VuZmlsdCAvIHBfcG9zdF9maWx0CmZpbmFsX25vcm1fcGxvdApgYGAKCioqRmlndXJlIDY6KiogUHJlIGFuZCBwb3N0IG5vcm1hbGl6ZWQgY291bnRzIHBlciBjZWxsICggcmFuZG9tbHkgc2FtcGxlIDEgY2VsbCBwZXIgYEdTTWApLiBUaGUgdXBwZXIgcGFuZWwgaW5kaWNhdGUgdGhlIHJhdyBjb3VudHMgZGlzdHJpYnV0aW9uIG9mIHRoZSByYW5kb21seSBzYW1wbGVkIGNlbGw7IFRoZSBtaWRkbGUgcGFuZWwgaW5kaWNhdGUgdGhlIG5vcm1hbGl6ZWQgY291bnRzIG9mIHRoZSByYW5kb21seSBzYW1wbGVkIGNlbGw7IFRoZSBib3R0b20gcGFuZWwgaW5kaWNhdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBub3JtYWxpemVkIGNvdW50cyBhZnRlciBleGNsdWRlIGFsbCBnZW5lcyB0aGF0IGV4cHJlc3NlZCBpbiBsZXNzIHRoYW4gMTAgY2VsbHM7IFgtYXhpcyBpbmRpY2F0ZSB0aGUgYEdTTXNgIHRoYXQgY2VsbCBpcyBzYW1wbGVkIGZyb207IFRoZSB2ZXJ0aWNhbCByZWQgZGFzaCBsaW5lIHNlcGFyYXRlIGNvbnRyb2wgKGxlZnQgc2lkZSBvZiByZWQgbGluZSksIGFuZCB0YmkgKHJpZ2h0IHNpZGUgb2YgcmVkIGxpbmUpLgoKQmVmb3JlIGZpbHRlcmluZywgdGhlIHJhdyBhbmQgbm9ybWFsaXplZCBjb3VudCBhcmUgYm90aCBpbmZsYXRlZCBhdCB6ZXJvIGR1ZSB0byB0aGUgc3BhcnNlIG5hdHVyZSBvZiBgc2Mvc25STkEtc2VxYC4gQWZ0ZXIgZmlsdGVyaW5nLCBub3JtYWxpemVkIGNvdW50cyB5aWVsZCBpbnRlcmVzdGluZyB2YXNlcyBzaGFwZSwgYSBmbGF0IGFuZCB3aWRlIGJhc2Ugd2l0aCBudW1iZXJzIG9mIGBiZWFkc2AgYXR0YWNoZWQgdXBvbi4gVGhlIGZsYXQgYmFzZSBpcyBsaWtlbHkgYmVjYXVzZSBvZiB0aGUgYHplcm8taW5mbGF0ZWRgIG5hdHVyZSBvZiBgc2Mvc25STkEtc2VxYCwgZXZlbiB0aG91Z2ggSSBleGNsdWRlIGFsbCBnZW5lIHRoYXQgZXhwcmVzc2VkIGluIGxlc3MgdGhhbiAxMCBjZWxscywgbW9zdCBnZW5lIHN0aWxsIGhhdmUgY291bnRzIGNsb3NlIHRvIHplcm8sIGZvbGxvd2luZyBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24uIEhvd2V2ZXIsIEkgZG9uJ3QgaGF2ZSBhIHdlbGwgY29uc3RydWN0ZWQgaHlwb3RoZXNpcyB0byBleHBsYWluIHRob3NlIGBiZWFkc2AgYXR0YWNoZWQsIG1heWJlIGJlY2F1c2UgdGhlIHJhdyBpbnB1dCAocmF3IGNvdW50cykgaW4gZGlzY3JldGUsIHRoZSBsb2ctTm9ybWFsaXplZCBjb3VudHMgd2lsbCBhbHNvIGJlIGRpc2NyZXRlLiBUaGUgcmVsYXRpdmUgaGVpZ2h0IG9mIHRoZSBiYXNlIGlzIHJlZmxlY3Rpbmcgc2VxdWVuY2UgZGVwdGggb2YgdGhvc2UgY2VsbC4gV2hlbiB0aG9zZSBjb3VudHMgY2xvc2UgdG8gemVybyB3ZXJlIG5vcm1hbGl6ZWQgYnkgdG90YWwgcmVhZCBvZiBhIGNlbGwsIHRoZSBsYXJnZXIgdGhlIHRvdGFsIHJlYWQsIHRoZSBzbWFsbGVyIHRoZSBub3JtYWxpemVkIGNvdW50LiBUaHVzIHRoZSBoaWdoZXIgdGhlIGJhc2UsIHRoZSBsb3dlciB0aGUgc2VxdWVuY2UgZGVwdGguCgoqKk5vdGUgOioqIFgtYXhpcyBpcyB0aGUgYEdTTWAgdGhvc2UgY2VsbHMgc2FtcGxlZCBmcm9tLCBub3QgYEdTTWAgaXRzZWxmLiBTbyB3ZSBjYW4ndCBpbmZlciBzZXF1ZW5jZSBkZXB0aCBvZiBgR1NNYCBmcm9tICoqRmlndXJlIDUqKi4KCiMjIExvZy1DUDEwawoKU2luY2UgSSBzZXQgdGhlIGBzY2FsZS5mYWN0b3JgIGFzIGAxMDAwMGAsIGl0J3Mgbm90IHBlciBtaWxsaW9uIGJ1dCBwZXIgMTBrLiBUbyBjYWxjdWxhdCB0aGUgb3ZlcmFsbCBzdGF0aXN0aWNzIG9mIExvZy1ub3JtYWxpemVkIGRhdGEsIGZpcnN0IGV4dHJhY3QgZXhwcmVzc2lvbiBkYXRhIHVzaW5nIGBzZXVyYXQ6OkdldEFzc2F5RGF0YSgpYCB0aGVuIGNhbGN1bGF0ZSB1c2luZyBgc3VtbWFyeSgpYC4KCioqTm90ZToqKiBhcHBseSBgYXMubWF0cml4KClgIHRvIGV4dHJhY3RlZCBleHByZXNzaW9uIG1hdHJpeCB3aWxsIGNvbnZlcnQgc3BhcnNlIG1hdHJpeCB0byBkZW5zZSBtYXRyaXgsIHdoaWNoIHJlcXVpcmVkIGFkZGl0aW9uYWwgYH40LjFHQmAgUkFNLiBVc2luZyBgQHhgIG9wZXJhdG9yIHdpbGwgZXhjbHVkZSBhbGwgemVybyB2YWx1ZSwgYnV0IG1lbW9yeSBlZmZpY2llbnQKCmBgYHtyfQojIDEuIEV4dHJhY3QgdGhlIG5vcm1hbGl6ZWQgc3BhcnNlIG1hdHJpeApub3JtX21hdHJpeCA8LSBHZXRBc3NheURhdGEoY29tYmluZWRfc2V1cmF0X2ZpbHRlcmVkLCBsYXllciA9ICJkYXRhIikKCiMgVGhpcyBzdW1tYXJpemVzIE9OTFkgdGhlIG5vbi16ZXJvIHZhbHVlcyBoaWRkZW4gaW5zaWRlIHRoZSBzcGFyc2UgbWF0cml4IHN0cnVjdHVyZQpzdW1tYXJ5KG5vcm1fbWF0cml4QHgpCmBgYAoKVGhlIHJpZ2h0LXNrZXdlZCBwcm9wZXJ0eSBpcyByZWZsZWN0ZWQgYnkgc21hbGwgZGlmZmVyZW5jZSBiZXR3ZWVuIGBNaW5gLCBgTWVkaWFuYCwgYW5kIGBNZWFuYC4KCiMjIEZyZWUgbWVtb3J5CgpgYGB7cn0KIyBEZWxldGUgbWVtb3J5IHBvaW50ZXIgZnJvbSBSCnJtKGNvbWJpbmVkX3NldXJhdCkKcm0oZGZfbm9ybV9maWx0KQpybShkZl9ub3JtX3VuZmlsdCkKcm0oZGZfcmF3KQpybShub3JtX21hdHJpeF9maWx0KQpybShub3JtX21hdHJpeF91bmZpbHQpCnJtKHJhd19jb3VudHNfYWxsKQpybShyYXdfbWF0cml4KQpybShzY19maWx0ZXJlZCkKcm0oc2NfdW5maWx0ZXJlZCkKcm0obWV0YV9kYXRhKQoKIyBEZSBhbGxvY2F0ZSB1bnVzZWQgbWVtb3J5CmdjKCkKYGBgCgojIyBaLXNjb3JlIE5vcm1hbGl6YXRpb24gJiBEaW1lbnRpb24gcmVkdWN0aW9uCgpUbyBhdm9pZCBgY3Vyc2Ugb2YgZGltZW50aW9uYWxpdHlgLCBtZWFuaW5nIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGV2ZXJ5IGRhdGEgcG9pbnQgaXMgYmFzaWNhbGx5IHRoZSBzYW1lIGluIGhpZ2ggZGltZW5zaW9uIGRhdGEsIGRpbWVuc2lvbiByZWR1Y3Rpb24gaXMgYW5vdGhlciBjb21tb24gc3RlcCB0byBjb25kdWN0IGJlZm9yZSBkaWZmZXJlbnRpYWwgZ2VuZSBhbmFseXNpcy4KCkZpcnN0IHBlcmZvcm0gYFotc2NvcmUgTm9ybWFsaXphdGlvbmAgdG8gbGluZWFybHkgc2hpZnQgdGhlIG1lYW4gZXhwcmVzc2lvbiBhY3Jvc3MgY2VsbHMgYXQgYDBgLCBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGFzIGAxYC4gVGhpcyBmdW5jdGlvbmFsbHkgaXMgd3JhcHBlZCBpbiBgU2NhbGVEYXRhKClgIGluIGBTZXVyYXRgLiBGb3IgdGhlIHNha2Ugb2YgbWVtb3J5LCBvbmx5IHNjYWxlIHRoZSB0b3AgMjAwMCBnZW5lIGRlZmluZWQgYnkgc2V1cmF0J3MgYEZpbmRWYXJpYWJsZUZlYXR1cmVzKClgIGZ1bmN0aW9uLgoKYGBge3Igei1zY29yZS1wbG90LCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTAsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KIyBGaW5kIHRoZSB0b3AgMjAwMCBnZW5lCmNvbWJpbmVkX3NldXJhdF9maWx0ZXJlZCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcygKICBjb21iaW5lZF9zZXVyYXRfZmlsdGVyZWQsIAogIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgCiAgbmZlYXR1cmVzID0gMjAwMAopCgojIFotc2NvcmUgbm9ybWFsaXphdGlvbgpjb21iaW5lZF9zZXVyYXRfZmlsdGVyZWQgPC0gU2NhbGVEYXRhKGNvbWJpbmVkX3NldXJhdF9maWx0ZXJlZCkKCiMgQ3JlYXRlIGEgbmV3IG1pbmktU2V1cmF0IG9iamVjdCBiYXNlZCBvbiB0aGUgRklMVEVSRUQgZGF0YXNldApzY19zY2FsZWQgPC0gc3Vic2V0KGNvbWJpbmVkX3NldXJhdF9maWx0ZXJlZCwgY2VsbHMgPSBzYW1wbGVkX2NlbGxzKQoKIyAyLiBFeHRyYWN0IFBvc3QtU2NhbGluZyBEYXRhIChaLXNjb3JlcykKIyBVc2UgbGF5ZXIgPSAic2NhbGUuZGF0YSIgdG8gcHVsbCB0aGUgbWF0cml4IHlvdSBqdXN0IGNyZWF0ZWQKc2NhbGVkX21hdHJpeCA8LSBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKHNjX3NjYWxlZCwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpKQoKZGZfc2NhbGVkIDwtIGFzLmRhdGEuZnJhbWUoc2NhbGVkX21hdHJpeCkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIkNlbGxfSUQiLCB2YWx1ZXNfdG8gPSAiU2NhbGVkX0V4cHJlc3Npb24iKSAlPiUKICAjIE5PVEU6IFdlIGRvIE5PVCBmaWx0ZXIgPiAwIGhlcmUuIE5lZ2F0aXZlIHZhbHVlcyBhcmUgaW1wb3J0YW50IFotc2NvcmVzIQogIG11dGF0ZShTYW1wbGUgPSBzY19zY2FsZWRAbWV0YS5kYXRhW0NlbGxfSUQsICJvcmlnLmlkZW50Il0pCgojIDMuIFBsb3QgdGhlIFNjYWxlZCBEaXN0cmlidXRpb24KcF9zY2FsZWQgPC0gZ2dwbG90KGRmX3NjYWxlZCwgYWVzKHggPSBTYW1wbGUsIHkgPSBTY2FsZWRfRXhwcmVzc2lvbiwgZmlsbCA9IFNhbXBsZSkpICsKICBnZW9tX3Zpb2xpbihzY2FsZSA9ICJ3aWR0aCIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjgpICsgCiAgZ2d0aXRsZSgiUG9zdC1TY2FsaW5nOiBaLXNjb3JlZCBFeHByZXNzaW9uICgxIENlbGwgcGVyIEdTTSkiKSArIAogIHlsYWIoIkV4cHJlc3Npb24gKFotc2NvcmUpIikgKyAKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpCiAgKSArIHZlcnRpY2FsX2xpbmUKCnBfc2NhbGVkCmBgYAoKKipGaWd1cmUgNzoqKiBFeHByZXNzaW9uIGxldmVsIGFmdGVyIFotc2NvcmUgbm9ybWFsaXphdGlvbi4gWS1heGlzIGlzIHRoZSBaLXNjb3JlIG5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBsZXZlbDsgWC1heGlzIGlzIGVhY2ggc2luZ2xlIGNlbGwgc2FtcGxlZCBmcm9tIGBHU01gOyBUaGUgdmVydGljYWwgcmVkIGRhc2ggbGluZSBzZXBhcmF0ZSBjb250cm9sIChsZWZ0IHNpZGUgb2YgcmVkIGxpbmUpLCBhbmQgdGJpIChyaWdodCBzaWRlIG9mIHJlZCBsaW5lKS4KClRoZW4gcGVyZm9ybSBwcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChgUENBYCksIGEgbGluZWFyIGRpbWVuc2lvbiByZWR1Y3Rpb24gYW5kIHBsb3QgY29ycmVzcG9uZGluZyBgTURTYCBwbG90IHVzaW5nIHRoZSBmaXJzdCB0d28gcHJpbmNpcGxlIGNvbXBvbmVudC4gVGhlIGBQQ0FgIGlzIGNvbmR1Y3QgdmlhIHNldXJhdCdzIGBSdW5QQ0EoKWAgZnVuY3Rpb24uIFRoZSBkZWZhdWx0IHBhcmFtZXRlciBjcmFzaGVkIFIgb24gbXkgbWFjaGluZSwgc28gZm9yIHRoZSBzYWtlIG9mIHBlcmZvcm1hbmNlLCBvbmx5IHJ1biBgUENBYCBvbiB0aGUgdG9wIDIwMDAgdmFyaWFibGUgZ2VuZXMgYW5kIHRvcCAyMCBQQ3MuCgpgYGB7ciBNRFMtcGxvdCwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTEwLCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDAsIG91dC53aWR0aD0iMTAwJSJ9CiMgUnVuIFBDQQpjb21iaW5lZF9zZXVyYXRfZmlsdGVyZWQgPC0gUnVuUENBKGNvbWJpbmVkX3NldXJhdF9maWx0ZXJlZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGNvbWJpbmVkX3NldXJhdF9maWx0ZXJlZCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5wY3MgPSAyMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpCgojIHZpc3VhbGl6ZSBNRFMgcGxvdApNRFNfcGxvdCA8LSBEaW1QbG90KGNvbWJpbmVkX3NldXJhdF9maWx0ZXJlZCwgcmVkdWN0aW9uID0gInBjYSIsIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIpCk1EU19wbG90CmBgYAoKKipGaWd1cmUgODoqKiBNRFMgcGxvdCBncm91cGVkIGJ5IGNvbmRpdGlvbi4gWS1heGlzIGlzIHRoZSBzZWNvbmQgUEM7IFgtYXhpcyBpcyB0aGUgZmlyc3QgUEM7IEVhY2ggZG90IGluZGljYXRlIGEgc2luZ2xlIGNlbGwuCgpOb3RlIGNvbnRyb2wgYW5kIHRiaSBncm91cCBhcmUgY2xlYXJseSBkaXN0aW5ndWlzaGVkIGF0IGZvdXIgdGlwIGJyYW5jaGVkIG91dCBmcm9tIGxvd2VyIGxlZnQgYnVsay4gV2hpY2ggaW1wbGllZCB0cmFuc2NyaXB0aW9uYWwgZGlmZmVyZW5jZSBpbiB0aGUgY2VsbHMgbG9jYXRlIGF0IHRoaXMgcmVnaW9uLgoKIyMgT2J0YWluIG5vcm1hbGl6ZWQgZGF0YWZyYW1lCgpgTm9ybWFsaXplRGF0YSgpYHN0b3JlZCBub3JtYWxpemVkIGNvdW50cyBpbiBgZGF0YWAgbGF5ZXIuIFNpbXBseSBjYWxsIGBTZXVyYXQ6OkdldEFzc2F5RGF0YSgpYCBvbiBgZGF0YWAgbGF5ZXIgdG8gb2J0YWluIG5vcm1hbGl6ZWQgY291bnRzLCBhbmQgY29udmVydCB0byBgZGF0YS5mcmFtZWAgdXNlIGBhcy5kYXRhLmZyYW1lKClgLgoKQWZ0ZXIgZmlsdGVyIGxvdyBxdWFsaXR5IGNlbGwgYXQgY2xlYW5pbmcgc3RlcCwgdGhlcmUgYXJlIGAyMTczNGAgY2VsbHMgcmVtYWluLiBBZnRlciBmaWx0ZXIgbG93IGV4cHJlc3NlZCBnZW5lIGF0IG5vcm1hbGl6YXRpb24gc3RlcCwgdGhlcmUgYXJlIGAyNTM4OWAgZ2VuZXMgcmVtYWluLiBUaHVzIHRoZSBzaGFwZSBvZiBmaW5hbCByZXN1bHRpbmcgZXhwcmVzc2lvbiBtYXRyaXggaXMgYDI1Mzg5ICogMjE3MzRgCgpgYGB7cn0KIyBleHRyYWN0IG5vcm1hbGl6ZWQgZGF0YQpub3JtX21hdHJpeCA8LSBHZXRBc3NheURhdGEoY29tYmluZWRfc2V1cmF0X2ZpbHRlcmVkLCBsYXllciA9ICJkYXRhIikKCiMgQ2hlY2sgZGltZW5zaW9ucyAoR2VuZXMgeCBDZWxscykKZGltKG5vcm1fbWF0cml4KQoKIyBDaGVjayByb3duYW1lCmhlYWQocm93bmFtZXMobm9ybV9tYXRyaXgpKQoKIyBDaGVjayBjb2xuYW1lCmhlYWQoY29sbmFtZXMobm9ybV9tYXRyaXgpKQoKIyBjb252ZXJ0IHRvIGRhdGEuZnJhbWUKIyBDb21tZW50IG91dCB0byBza2lwIGR1ZSB0byBtZW1vcnkgY29uc3RyYWluCiMgVGhlIHNjcmlwdCBiZWxvdyBwcm9tcHQgdG8gYWxsb2NhdGUgNS40IEdNIG9mIFJBTQojIG5vcm1fZGYgPC0gYXMuZGF0YS5mcmFtZShub3JtX21hdHJpeCkKIyBDaGVjayBkaW1lbnNpb25zIChHZW5lcyB4IENlbGxzKQojIGRpbShub3JtX2RmKQpgYGAKClRoZSBmaW5hbCByZXN1bHQgaXMgaW4gYGRnQ01hdHJpeGAgaW5zdGVhZCBvZiBgZGF0YS5mcmFtZWAgZHVlIHRvIG1lbW9yeSBjb25zdHJhaW4uIENvbnZlcnQgdG8gYGRhdGEuZnJhbWVgIHJlcXVpcmUgYWRkaXRpb25hbCBgfjUuNCBHQmAgb2YgUkFNLgoKIyBEaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uCgpEaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIHdhcyBjb25kdWN0IHVzaW5nIGBTZXVyYXQ6OkZpbmRNYXJrZXJzKClgIGZ1bmN0aW9uLgoKIyMgRmluZCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZQoKUmVmZXIgdG8gb3JpZ2luYWwgcGFwZXIgKFtHYXJ6YSBldCBhbCwgMjAyM10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jZWxyZXAuMjAyMy4xMTMzOTUpKSwgdGhleSB1c2UgYFdpbGNveG9uIFJhbmstU3VtIHRlc3RgIHRvIGlkZW50aWZ5IERFcywgYEJvbmZlcnJvbmkgY29ycmVjdGlvbmAgdG8gYWRqdXN0IGBwX3ZhbHVlYCwgYW5kIHNldCB0aGUgYWRqdXN0ZWQgcF92YWx1ZSB0aHJlc2hvbGQgdG8gYmUgYDAuMDFgLgoKYGBge3J9CiMgSW5zdGFsbCBwcmVzdG8gcGFja2FnZSBmb3IgZmFzdGVyIFdpbGNveG9uIFJhbmsgU3VtIFRlc3QKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdpbW11bm9nZW5vbWljcy9wcmVzdG8nKQoKIyBGaW5kIERFcyBiZXR3ZWVuIGNvbnRvcmwgYW5kIHRiaSBncm91cApDb25kaXRpb25fREVfbGlzdCA8LSBGaW5kTWFya2Vycyhjb21iaW5lZF9zZXVyYXRfZmlsdGVyZWQsIGlkZW50LjEgPSAiY29udHJvbCIsIGlkZW50LjIgPSAidGJpIiwgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIikKCiMgQ2FsY3VsYXRlIHRoZSBjb3VudHMgYnkgdGVtcG9yYXJpbHkgc3Vic2V0dGluZyB0aGUgZnVsbCwgdW5maWx0ZXJlZCBsaXN0Cm51bV9wdmFsXzA1IDwtIG5yb3coc3Vic2V0KENvbmRpdGlvbl9ERV9saXN0LCBwX3ZhbCA8IDAuMDUpKQpudW1fcGFkal8wNSA8LSBucm93KHN1YnNldChDb25kaXRpb25fREVfbGlzdCwgcF92YWxfYWRqIDwgMC4wNSkpCm51bV9wYWRqXzAxIDwtIG5yb3coc3Vic2V0KENvbmRpdGlvbl9ERV9saXN0LCBwX3ZhbF9hZGogPCAwLjAxKSkKCm1lc3NhZ2UoIk51bWJlciBvZiBnZW5lcyB3aXRoIHJhdyBwX3ZhbCA8IDAuMDU6ICIsIG51bV9wdmFsXzA1KQptZXNzYWdlKCJOdW1iZXIgb2YgZ2VuZXMgd2l0aCBwX3ZhbF9hZGogPCAwLjA1OiAiLCBudW1fcGFkal8wNSkKbWVzc2FnZSgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqIDwgMC4wMTogIiwgbnVtX3BhZGpfMDEpCgojIGFwcGx5IHRocmVzaG9sZApDb25kaXRpb25fREVfbGlzdCA8LSBzdWJzZXQoQ29uZGl0aW9uX0RFX2xpc3QsIHBfdmFsX2FkaiA8IDAuMDEpCgojIEV4YW1pbiBERSBMSVNUCmhlYWQoQ29uZGl0aW9uX0RFX2xpc3QpCmBgYAoKUm93cyBpbmRpY2F0ZSBIVUdPIHN5bWJvbDsgQ29sdW1ucyBpbmRpY2F0ZToKCi0gICBwX3ZhbDogcCB2YWx1ZQotICAgYXZnX2xvZzJGQzogYXZlcmFnZSBsb2cgZm9sZCBjaGFuZ2UsIHBvc2l0aXZlIG1lYW5zIHVwcmVndWxhdGVkIGluIGNvbnRyb2wKLSAgIHBjdC4xOiBwZXJjZW50YWdlIG9mIGNlbGwgZXhwcmVzc2luZyB0aGlzIGdlbmUgaW4gY29udHJvbCBncm91cAotICAgcGN0LjI6cGVyY2VudGFnZSBvZiBjZWxsIGV4cHJlc3NpbmcgdGhpcyBnZW5lIGluIHRiaSBncm91cAotICAgcF92YWxfYWRqOiBBZGp1c3RlZCBwLXZhbHVlLCBiYXNlZCBvbiBib25mZXJyb25pIGNvcnJlY3Rpb24gdXNpbmcgYWxsIGdlbmVzIGluIHRoZSBkYXRhc2V0CgojIyBWb2xjYW5vIHBsb3QKCmBgYHtyIFZvbGNhbm8tcGxvdCwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTEwLCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDAsIG91dC53aWR0aD0iMTAwJSJ9CmxpYnJhcnkoZ2dyZXBlbCkKCiMgMS4gUHJlcGFyZSB0aGUgZGF0YWZyYW1lCmRmX3ZvbGNhbm8gPC0gQ29uZGl0aW9uX0RFX2xpc3QgJT4lCiAgIyBNb3ZlIHJvdyBuYW1lcyB0byBhIEdlbmUgY29sdW1uCiAgbXV0YXRlKEdlbmUgPSByb3duYW1lcyguKSkgJT4lCiAgIyBQcmV2ZW50IC1sb2cxMCgwKSBmcm9tIHByb2R1Y2luZyBpbmZpbml0ZSB2YWx1ZXMgYW5kIGJyZWFraW5nIHRoZSBwbG90CiAgbXV0YXRlKHBfdmFsX2FkaiA9IGlmZWxzZShwX3ZhbF9hZGogPT0gMCwgLk1hY2hpbmUkZG91YmxlLnhtaW4sIHBfdmFsX2FkaikpICU+JQogIAogICMgU3RyaWN0IHBfdmFsX2FkaiA8IDAuMDEKICBtdXRhdGUoU2lnbmlmaWNhbmNlID0gY2FzZV93aGVuKAogICAgcF92YWxfYWRqIDwgMC4wMSAmIGF2Z19sb2cyRkMgPiAwIH4gIkRvd25yZWd1bGF0ZWQgaW4gVEJJIiwKICAgIHBfdmFsX2FkaiA8IDAuMDEgJiBhdmdfbG9nMkZDIDwgMCB+ICJVcHJlZ3VsYXRlZCBpbiBUQkkiLAogICAgVFJVRSB+ICJOb3QgU2lnbmlmaWNhbnQiCiAgKSkKCiMgMi4gRXh0cmFjdCB0aGUgdG9wIDEwIFVwcmVndWxhdGVkIGFuZCB0b3AgMTAgRG93bnJlZ3VsYXRlZCBnZW5lcyB0byBsYWJlbAojIChTdGlsbCBmaW5kaW5nIHRoZSBleHRyZW1lcyBiYXNlZCBvbiBGb2xkIENoYW5nZSwgYnV0IHB1bGxpbmcgZnJvbSB0aGUgbmV3IFNpZ25pZmljYW5jZSBncm91cHMpCnRvcF91cCA8LSBkZl92b2xjYW5vICU+JSBmaWx0ZXIoU2lnbmlmaWNhbmNlID09ICJEb3ducmVndWxhdGVkIGluIFRCSSIpICU+JSB0b3BfbigxMCwgd3QgPSBhdmdfbG9nMkZDKQp0b3BfZG93biA8LSBkZl92b2xjYW5vICU+JSBmaWx0ZXIoU2lnbmlmaWNhbmNlID09ICJVcHJlZ3VsYXRlZCBpbiBUQkkiKSAlPiUgdG9wX24oLTEwLCB3dCA9IGF2Z19sb2cyRkMpCnRvcF9nZW5lc190b19sYWJlbCA8LSBiaW5kX3Jvd3ModG9wX3VwLCB0b3BfZG93bikKCiMgMy4gR2VuZXJhdGUgdGhlIFZvbGNhbm8gUGxvdCAKdm9sY2Fub19wbG90IDwtIGdncGxvdChkZl92b2xjYW5vLCBhZXMoeCA9IGF2Z19sb2cyRkMsIHkgPSAtbG9nMTAocF92YWxfYWRqKSwgY29sb3IgPSBTaWduaWZpY2FuY2UpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNiwgc2l6ZSA9IDEuNSkgKwogICMgQ3VzdG9tIGNvbG9ycwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKAogICAgIlVwcmVndWxhdGVkIGluIFRCSSIgPSAiIzAwNzJCMiIsIAogICAgIkRvd25yZWd1bGF0ZWQgaW4gVEJJIiA9ICIjRDU1RTAwIiwKICAgICJOb3QgU2lnbmlmaWNhbnQiID0gImdyZXk4MCIKICApKSArCiAgIyBBZGQgbGFiZWxzIG9ubHkgZm9yIHRoZSB0b3AgZ2VuZXMKICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvcF9nZW5lc190b19sYWJlbCwgYWVzKGxhYmVsID0gR2VuZSksIAogICAgICAgICAgICAgICAgICBzaXplID0gMy41LCBib3gucGFkZGluZyA9IDAuNSwgbWF4Lm92ZXJsYXBzID0gSW5mLCBjb2xvciA9ICJibGFjayIpICsKICAgICAgICAgICAgICAgICAgCiAgIyBVUERBVEVEIFRIUkVTSE9MRCBMSU5FUzogCiAgIyBIb3Jpem9udGFsIGxpbmUgYXQgMC4wMSBwLXZhbHVlIGN1dG9mZgogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjAxKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArCiAgIyBTaW5nbGUgdmVydGljYWwgbGluZSBhdCAwIChzaW5jZSBtYWduaXR1ZGUgbm8gbG9uZ2VyIG1hdHRlcnMpCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArCiAgCiAgdGhlbWVfY2xhc3NpYygpICsKICBnZ3RpdGxlKCJWb2xjYW5vIFBsb3Q6IFRCSSB2cy4gQ29udHJvbCAocF92YWxfYWRqIDwgMC4wMSkiKSArCiAgeGxhYigiQXZlcmFnZSBMb2cyIEZvbGQgQ2hhbmdlIikgKwogIHlsYWIoIi1Mb2cxMChBZGp1c3RlZCBQLXZhbHVlKSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwgCiAgICAKICAgICMgSW5jcmVhc2VzIHRoZSBzaXplIG9mIHRoZSBMZWdlbmQgVGl0bGUgKCJFeHByZXNzaW9uIiwgIklkZW50aXR5IikKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgCiAgICAjIEluY3JlYXNlcyB0aGUgc2l6ZSBvZiB0aGUgTGVnZW5kIFRleHQgKHRoZSBudW1iZXJzIGFuZCBncm91cCBuYW1lcykKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAKICAgICMgSW5jcmVhc2VzIHRoZSBzaXplIG9mIHRoZSBNYWluIFRpdGxlCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCgp2b2xjYW5vX3Bsb3QKYGBgCgoqKkZpZ3VyZSA5OioqIE1EUyBwbG90IGdyb3VwZWQgYnkgY29uZGl0aW9uLiBZLWF4aXMgaXMgbmVnYXRpdmUgbG9nLXRyYW5zZm9ybWVkIGFkanVzdGVkIHAgdmFsdWU7IFgtYXhpcyBpcyB0aGUgYXZlcmFnZSBsb2cgZm9sZCBjaGFuZ2UgY2VudGVyZWQgYXQgemVybzsgQmx1ZSBkb3RzIG9uIHRoZSBsZWZ0IGluZGljYXRlIGdlbmVzIHRoYXQgZG93bnJlZHVsYXRlZCBpbiB0YmkgZ3JvdXA7IE9yYW5nZSBib3RzIG9uIHRoZSByaWdodCBpbmRpY2F0ZSBnZW5lcyB0aGF0IHVwcmVndWxhdGVkIGluIHRiaSBncm91cDsgVGhlIGhvcml6b250YWwgYmxhY2sgZGFzaCBsaW5lIGluZGljYXRlIGFkanVzdGVkIHAgdmFsdWUgPSAwLjAxIHRocmVzaG9sZC4gMTAgbW9zdCB1cCBhbmQgZG93biByZWd1bGF0ZWQgZ2VuZSAoanVlZGdlIGJ5IHRoZSBsYXJnZXIgYWJzb2x1dGUgYXZlcmFnZSBsb2cgZm9sZCBjaGFuZ2UpIGFyZSB0YWdnZWQgYnkgdGhlaXIgSFVHTyBzeW1ib2wuCgpUaGUgYWRqdXN0ZWQgcCB2YWx1ZSBvZiBkb3ducmVndWxhdGVkIGdlbmUgaXMgbXVjaCBoaWdoZXIgdGhhbiBkb3ducmVndWxhdGVkIGdlbmUsIGJ1IHRoZSByZWFzb24gcmVtYWluIHVua25vd24uIEludGVyZXN0aW5nbHksIHRoZSBsZWZ0IG1vc3QgdXByZWd1bGF0ZSBnZW5lIGBYSVNUYCBpcyBrbm93biB0byBvbmx5IGV4cHJlc3MgaW4gZmVtYWxlLCBvYnNlcnZpbmcgYFhJU1RgIGFzIHVwcmVndWxhdGUgZ2VuZSBpbiB0YmkgZ3JvdXAgcmVmbGVjdCB0aGUgYmlhc2VkIHNleCBjb21waXNpdGlvbiBiZXR3ZWVuIGNvbnRyb2wgKGFsbCA1IHNhbXBsZSBhcmUgbWFsZSkgYW5kIHRiaSAoIDIgb3V0IG9mIDExIGFyZSBmZW1hbGUpIGdyb3VwLgoKIyMgSGVhdG1hcAoKVGhlIHRvcCAyMCBnZW5lLCBkZWZpbmVkIGJ5IHRoZSBsb3dlc3QgYWRqdXN0ZWQgcCB2YWx1ZSB3YXJlIHBsb3R0ZWQgYXMgaGVhdG1hcC4KCmBgYHtyIEhlYXRtYXAtcGxvdCwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTEwLCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDAsIG91dC53aWR0aD0iMTAwJSJ9CiMgMS4gSWRlbnRpZnkgdGhlIHRvcCAyMCBtb3N0IHNpZ25pZmljYW50IGdlbmVzIChsb3dlc3QgcF92YWxfYWRqKQp0b3AyMF9nZW5lcyA8LSBkZl92b2xjYW5vICU+JQogIGFycmFuZ2UocF92YWxfYWRqKSAlPiUgIyBTb3J0IGZyb20gc21hbGxlc3QgcC12YWx1ZSB0byBsYXJnZXN0CiAgaGVhZCgyMCkgJT4lICAgICAgICAgICAjIEdyYWIgdGhlIHRvcCAyMCByb3dzCiAgcHVsbChHZW5lKSAgICAgICAgICAgICAjIEV4dHJhY3QganVzdCB0aGUgZ2VuZSBuYW1lcwoKIyAyLiBNZW1vcnktU2FmZSBTY2FsaW5nCiMgT25seSBzY2FsZSB0aGVzZSAyMCBzcGVjaWZpYyBnZW5lcyBhY3Jvc3MgdGhlIGNlbGxzIHNvIFIgZG9lc24ndCBjcmFzaCEKY29tYmluZWRfc2V1cmF0X2ZpbHRlcmVkIDwtIFNjYWxlRGF0YShjb21iaW5lZF9zZXVyYXRfZmlsdGVyZWQsIGZlYXR1cmVzID0gdG9wMjBfZ2VuZXMpCgojIDMuIEdlbmVyYXRlIHRoZSBIZWF0bWFwCmhlYXRtYXBfcGxvdCA8LSBEb0hlYXRtYXAoCiAgb2JqZWN0ID0gY29tYmluZWRfc2V1cmF0X2ZpbHRlcmVkLAogIGZlYXR1cmVzID0gdG9wMjBfZ2VuZXMsCiAgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIiwgIyBUaGlzIHNlcGFyYXRlcyB0aGUgY2VsbHMgaW50byBUQkkgYW5kIENvbnRyb2wgYmxvY2tzCiAgc2l6ZSA9IDQsICAgICAgICAgICAgICAgIyBTaXplIG9mIHRoZSB0b3AgZ3JvdXAgbGFiZWxzCiAgYW5nbGUgPSA0NSAgICAgICAgICAgICAgIyBBbmdsZSBvZiB0aGUgZ3JvdXAgbGFiZWxzCikgKyAKICBnZ3RpdGxlKCJIZWF0bWFwIG9mIFRvcCAyMCBEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMiKSArCiAgIyBBZGp1c3QgdGhlIFktYXhpcyB0ZXh0IHNpemUgc28gdGhlIGdlbmUgbmFtZXMgYXJlIHJlYWRhYmxlCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkKCmhlYXRtYXBfcGxvdApgYGAKCioqRmlndXJlIDEwOioqIEhlYXRtYXAgb2YgdG9wIDIwIGdlbmUgd2l0aCBsb3dlc3QgYWRqdXN0ZWQgcCB2YWx1ZS4gWS1heGlzIGluZGljYXRlIHRoZSBIT0dPIHN5bWJvbCBvZiBzZWxlY3RlZCBnZW5lczsgRWFjaCB0aW15IGNvbHVtbiBhbG9uZyBYLWFzaXggaXMgYSBjZWxsOyBUaGUgbGVmdCBoYWxmIGluZGljYXRlIHRoZWlyIGV4cHJlc3Npb24gbGV2ZWwgaW4gY29udHJvbCBncm91cDsgVGhlIHJpZ2h0IGhhbGYgaW5kaWNhdGUgdGhlaXIgZXhwcmVzc2lvbiBsZXZlbCBpbiB0YmkgZ3JvdXAuCgpUaGUgaGVhdG1hcCBkb2Vzbid0IHNob3cgY2xlYXIgY2x1c3RlciBiZXR3ZWVuIGNvbnRyb2wgYW5kIHRiaSBncm91cC4gVGhlcmUgaXMgbWlub3IgYnJpZ2h0bmVzcyBkaWZmZXJlbmNlIHdpdGggY29udHJvbCBncm91cCBiZWluZyBkYXJrZXIsIGluZGljYXRlIG92ZXJhbGwgaGlnaGVyIGV4cHJlc3Npb24gbGV2ZWwgaW4gY29udHJvbCBncm91cC4gT25lIGh5cG90aGVzaXMgaXMgdGhhdCBzYW1wbGVzIGluIHRiaSBncm91cCBhcmUgbW9yZSBsaWtlbHkgdG8gbG9zcyBjZWxsIGludGVncmF0eSBhbmQgZXhwZXJpZW5jZSBSTkEgbGVha2FnZSByZXN1bHQgZnJvbSBpbXBhY3QgZGFtYWdlLgoKIyBDb25jbHVzdGlvbgoKVGhpcyBwcm9qZWN0IHV0aWxpemVkIGRhdGFzZXQgYEdTRTIwOTU1MmAgdG8gZXhhbWluZSBwb3RlbnRpYWwgdHJhbnNjcmlwdG9tZSBkaWZmZXJlbmNlIGJldHdlZW4gbm9uLW5ldXJvbG9naWNhbCBkZWF0aHMgKGNvbnRyb2wpIGFuZCBUcmF1bWF0aWMgYnJhaW4gaW5qdXJ5ICh0YmkpIGF0IHNpbmdsZSBjZWxsIHJlc29sdXRpb24uIFN1YnBvcHVsYXRpb24gZGlmZmVyZW5jZSBiZXR3ZWVuIGNvbnRyb2wgYW5kIHRiaSBncm91cCB3YXMgb2JzZXJ2ZWQsIHJldmVhbGVkIGJ5IE1EUyBwbG90LCB3aGVyZSB0YmkgZ3JvdXAgdGVuZCB0byBjb25jZW50cmF0ZSBvbiBmb3VyIHRpcHMgYnJhY2ggb3V0IGZyb20gbG93ZXItbGVmdCBidWxrLiBgMTIyMjNgIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lIHdlcmUgaWRlbnRpZmllZCBieSBjYWxsaW5nIGBzZXVyYXQ6RmluZE1hcmtlcigpYCBpbmRpY2F0ZSB0aGUgZXhpc3RhbmNlIG9mIGRpZmZlcmVuY2UgYmV0d2VlbiBjb250cm9sIGFuZCB0YmkgZ3JvdXAuIEhvd2V2ZXIsIHRoZXJlIGlzIG5vdCBlbm91Z2ggZXZpZGVuY2UgdG8gaW5mZXIgdGhlIGZ1bmN0aW9uYWwgYW5kL29yIHBhdGh3YXkgZGlmZmVyZW5jZSBhbW9uZyBjb250b3JsIGFuZCB0YmkgZ3JvdXAuCgojIFF1ZXN0aW9uCgoxLiAgV2h5IGlzIHRoZSBkYXRhc2V0IG9mIGludGVyZXN0IHRvIHlvdT8KCi0gICBJIHdhcyBpbml0aWFsbHkgd2FudCB0byBpbnZlc3RpZ2F0ZSB0aGUgZGF0YXNldCwgW0dTRTEyMTY1NF0oaHR0cHM6Ly93d3ctbmNiaS1ubG0tbmloLWdvdi5teWFjY2Vzcy5saWJyYXJ5LnV0b3JvbnRvLmNhL2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0UxMjE2NTQpLCB0aGF0IEkgY2FtZSBhY3Jvc3MgaW4gcHJldmlvdXMgcHJvamVjdCwgYnV0IGl0J3Mgbm90IGEgaHVtYW4gZGF0YS4gU28gaSBkZWNpZGUgdG8gY2hvc2UgYW5vdGhlciBodW1hbiBkYXRhc2V0IHdpdGggc2ltaWxhciBjb250ZXh0LiBGb3IgZnV0aGVyIGRldGFpbCwgcGxlYXNlIHJlZmVyIHRvIG15IFtqb3VybmFsXShodHRwczovL2dpdGh1Yi5jb20vYmNiNDIwLTIwMjYvSmlhcWlfTWEvd2lraS9Bc3NpZ25tZW50LSUyMzEpLgoKMi4gIFdoYXQgYXJlIHRoZSBjb250cm9sIGFuZCB0ZXN0IGNvbmRpdGlvbnMgb2YgdGhlIGRhdGFzZXQ/CgotICAgVGhlIGNvbnRyb2wgaXMgbm9uLW5ldXJvbmFsIGRlYXRoIHBvc3Rtb3J0ZW0gYnJhaW4gc2FtcGxlczsgVGhlIHRlc3QgaXMgc3VyZ2ljYWxseSBldmFjdWF0ZWQgdGlzc3VlIGZyb20gVHJhdW1hdGljIGJyYWluIGluanVyeSAoVEJJKSBjYXNlcy4KCiogTGluayB0byBzZWN0aW9uOiBbRGF0YSBEb3dubG9hZF0oI2RhdGEtZG93bmxvYWQpCgozLiAgSG93IG1hbnkgc2FtcGxlcyBpbiBlYWNoIG9mIHRoZSBjb25kaXRpb25zIG9mIHlvdXIgZGF0YXNldD8KCi0gICA1IHNhbXBsZXMgKEdTTXMpLCAxMDY2NiBjZWxscyBpbiBjb250cmw7IDEyIHNhbXBsZXMgKEdTTXMpLCAxMzk1NSBjZWxscyBpbiB0YmkKCiogTGluayB0byBzZWN0aW9uOiBbRGF0YSBleHBsb3JpYXRpb25dKCNkYXRhLWV4cGxvcmlhdGlvbikKCjQuICBXZXJlIHRoZXJlIGV4cHJlc3Npb24gdmFsdWVzIHRoYXQgd2VyZSBub3QgdW5pcXVlIGZvciBzcGVjaWZpYyBnZW5lcz8gSG93IGRpZCB5b3UgaGFuZGxlIHRoZXNlPwoKLSAgIE5vLCB0aGUgcmF3IGRhdGEgaXMgYWxyZWFkeSBpbiBIT0dPIHN5bWJvbCwgdGh1cyB3b24ndCBoYXZlIGR1cGxpY2F0ZS4KCiogTGluayB0byBzZWN0aW9uOiBbTWFwcGluZyB0byBIVUdPIHN5bWJvbHNdKCNtYXBwaW5nLXRvLWh1Z28tc3ltYm9scykKCjUuICBXZXJlIHRoZXJlIGV4cHJlc3Npb24gdmFsdWVzIHRoYXQgY291bGQgbm90IGJlIG1hcHBlZCB0byBjdXJyZW50IEhVR08gc3ltYm9scz8KCi0gICBObywgdGhlIHJhdyBkYXRhIGlzIGFscmVhZHkgaW4gSE9HTyBzeW1ib2wsIHdpdGggMTAwJSBtYXBwaW5nLgoKKiBMaW5rIHRvIHNlY3Rpb246IFtNYXBwaW5nIHRvIEhVR08gc3ltYm9sc10oI21hcHBpbmctdG8taHVnby1zeW1ib2xzKQoKNi4gIFdlcmUgdGhlcmUgYW55IG91dGxpZXJzIGluIHlvdXIgZGF0YXNldD8gSG93IHdlcmUgdGhleSBoYW5kbGVkIGluIHRoZSBvcmlnaW5hdGluZyBwYXBlcj8gSG93IG1hbnkgb3V0bGllcnMgd2VyZSByZW1vdmVkPwoKKiBZZXM7IENlbGxzIHdoaWNoIGhhdmUgbG93ZXIgdGhhbiB0aHJlZSBtZWFuIGFic29sdXRlIGRldmlhdGlvbiBmcm9tIHRoZSBtZWRpYW4gbnVtYmVyIG9mIHJlYWRzIHByZXNlbnQgaW4gdGhlIHNhbXBsZSB3ZXJlIHJlbW92ZWQgYW5kIGNlbGxzIHdoaWNoIGhhcyBsZXNzIHRoYW4gMTAwMCBnZW5lIGRldGVjdGVkIHdlcmUgcmVtb3ZlZCBpbiBvcmlnaW5hbCBwYXBlciAoW0dhcnphIGV0IGFsLCAyMDIzXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNlbHJlcC4yMDIzLjExMzM5NSkpLiBJbiB0b3RhbCwgYDk1MTEvMjQ2MjEgPSAzOC42JWAgY2VsbHMgd2VyZSByZW1vdmVkLgoKNy4gIEhvdyBkaWQgeW91IGhhbmRsZSByZXBsaWNhdGVzPwoKKiBUaGVyZSBhcmUgbm8gcmVwbGljYXRlLCBvbmUgYEdTTWAgaXMgc2FtcGxlZCBmcm9tIG9uZSBwYXRpZW50IChbR2FyemEgZXQgYWwsIDIwMjNdKCNtYXBwaW5nLXRvLWh1Z28tc3ltYm9scykpLgoKOC4gIFdoYXQgaXMgdGhlIGZpbmFsIGNvdmVyYWdlIG9mIHlvdXIgZGF0YXNldD8KCiogYDI1Mzg5IC8gMzM1MzggPSAwLjc1NzAyMTlgCgo5LiAgV2hpY2ggbm9ybWFsaXphdGlvbiBtZXRob2QgZGlkIHlvdSB1c2UgYW5kIHdoeT8KCiogTG9nLW5vcm1hbGl6YXRpb24sIHdpdGggc2NhbGUuZmFjdG9yIHNldCB0byBiZSBgMTAwMDBgLCBzbyBiYXNpY2FsbHkgQ00xMGsgdG8gYWNjb3VudCBmb3IgYHNjL3NuUk5BIHNlcWAncyByZWxhdGl2ZWx5IHNtYWxsIGxpYnJhcnkgc2l6ZSBjb21wYXJlIHdpdGggYEJ1bGsgUk5BLXNlcWAuIFV0aWxpemVkIGJlY2F1c2UgaXQncyBlYXN5LCBpbnR1aXRpdmUsIHNldCBhcyBiYXNsaW5lIGZyb20gbXVsdGlwbGUgbm9ybWFsaXphdGlvbiBiZW5jaG1hcmsgcmVwb3J0IChbR2UgZXQgYWwsIDIwMjVdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMzcxL2pvdXJuYWwucG9uZS4wMzM1MTAyKSwgW0x5dGFsIGV0IGFsLCAyMDIwXShodHRwczovL2RvaS5vcmcvMTAuMzM4OS9mZ2VuZS4yMDIwLjAwMDQxKSwgW0FtZXpxdWl0YSBldCBhbCwgMjAxOV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvczQxNTkyLTAxOS0wNjU0LXgpKSwgYW5kIGFsc28gdGhlIG1ldGhvZCB1c2VkIGluIG9yaWdpbmFsIHBhcGVyIChbR2FyemEgZXQgYWwsIDIwMjNdKCNtYXBwaW5nLXRvLWh1Z28tc3ltYm9scykpLgoKKiBMaW5rIHRvIHNlY3Rpb246IFtOb3JtYWxpemF0aW9uXSgjbm9ybWFsaXphdGlvbikKCjEwLiBIb3cgbWFueSBnZW5lcyB3ZXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkPyBXaGF0IHRocmVzaG9sZHMgZGlkIHlvdSB1c2UgYW5kIHdoeT8KCiogYDEyNDE1YCBnZW5lcyB3ZXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIChhZGp1c3RlZCBwIHZhbHVlIDwgMC4wNSksIGFmdGVyIHRocmVzaG9sZCBhdCBgMC4wMWAsIGAxMjIyM2AgZ2VuZSByZW1haW47IGAwLjAxYCBpcyBjaG9zZW4gYmVjYXVzZSBpdCdzIHRoZSB0aHJlc2hvbGQgYXBwbGllZCBpbiBvcmlnaW5hbCBwYXBlciAoW0dhcnphIGV0IGFsLCAyMDIzXSgjbWFwcGluZy10by1odWdvLXN5bWJvbHMpKS4KCjExLiBXaGljaCBtZXRob2QgZGlkIHlvdSB1c2UgdG8gYWRqdXN0IHAgdmFsdWU/IEFuZCBXaHk/IEhvdyBtYW55IGdlbmVzIHBhc3NlZCBjb3JyZWN0aW9uPwoKKiBgQm9uZmVycm9uaSBjb3JyZWN0aW9uYCwgYmVjYXVzZSBpdCdzIHRoZSBkZWZhdWx0IHNldXJhdCBwYXJhbWV0ZXIgYW5kIGFsc28gdGhlIG1ldGhvZCBhcHBsaWVkIGluIG9yaWdpbmFsIHBhcGVyIChbR2FyemEgZXQgYWwsIDIwMjNdKCNtYXBwaW5nLXRvLWh1Z28tc3ltYm9scykpLgoKKiBMaW5rIHRvIHNlY3Rpb246IFtGaW5kIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lXSgjZmluZC1kaWZmZXJlbnRpYWxseS1leHByZXNzZWQtZ2VuZSkKCjEyLiBEbyB5b3UgY29uZGl0aW9ucyBjbHVzdGVyIHRvZ2V0aGVyIGF0IGhlYXRtYXA/IEV4cGxhaW4gd2h5IG9yIHdoeSBub3QuCgoqIE5vLCB0aGV5IGFyZSB3ZWFrbHkgY2x1c3RlcmVkIHdpdGggbWlub3IgYnJpZ2h0bmVzcyBkaWZmZXJlbmNlLiBJIGRvbid0IGhhdmUgYW4gZm9ybXVsYXRlZCBleHBsYW5hdGlvbiBmb3IgdGhhdCwgSSdtIGV4cGVjdGluZyBpdCB0byBiZSBzdHJvbmdseSBjbHVzdGVyZWQuCgojIFJlZmVyZW5jZXMgeyNyZWZlcmVuY2VzfQ==